先分享一下我的面向對象分析方法
- 找出最關鍵的一些業務場景;一般通過動詞來尋找,比如招聘系統中,一個應聘人投遞一個職位就是一次應聘,應聘就是一個業務場景;一個學生參加某門課的考試,那么考試就是一個業務場景;一個學生去圖書館借書,那么借書就是一個業務場景;
- 針對每個業務場景分析出有哪些場景參與者,哪些參與者以對象的形式參與,哪些參與者以服務的形式參與;為什么要區分對象還是服務是因為有時候我們不關心參與者是哪個,而只關心參與者是什么。一般服務在系統中我們只關心它是什么服務,并且在系統中服務一般也只有一個實例;而對象則不同,我們會關心對象是誰,即哪一個;
- 分析每個場景參與者對象的基本狀態特征;所謂的基本狀態特征是指對象與生俱來的,對象從一開始被創建出來之后就具有的狀態特征;最形象的例子就是人的身高體重,當人一出生便具有了身高和體重這兩個狀態特征;再比如一篇博文,它從被寫好之時起就具有了內容這個狀態特征,但是我們可以隨便修改博文的內容;但是有些狀態特征是不能修改的,比如博文的創建時間一旦博文被創建之后便不能再被修改;需要注意的是我們不要把和對象關聯的一些關聯信息也認為是對象的基本狀態特征。比如某人有一本英語六級考試證書,那么人和證書之間的關系是擁有的關系,證書不是人固有的與生俱來的基本狀態特征,而是人參與了某次考試這個場景后得來的;后面我會提到這種關聯關系該如何去思考和理解!
- 分析每個場景參與者對象分別扮演什么角色參與場景,整個場景的完整交互過程是怎樣的,對象在參與場景的過程中執行了哪些交互行為; 相信大家都明白這點非常重要,因為它涉及到對象之間如何交互,涉及到該如何分析哪個對象該具有哪個交互職責;從而最終決定了在代碼級別哪個類該具有哪些方法的問題。關于這方面思考的例子我會在下面進行介紹,這里先說一下理論吧。我覺得要點是將整個業務場景中的每個交互行為通過四色原型的分析方法來理解。用一句話來概括四色原型就是:一個什么什么樣的人或組織或物品以某種角色在某個時刻或某段時間內參與某個活動。另外一個技巧就是,我們經常可以問自己,這個交互行為是“誰通知誰做什么事情?“,行為的驅動者是誰?行為的執行者是誰?一般行為的驅動者就是通知方,行為的執行者就是被通知方,被通知方擁有”通知方要求做的事情“執行行為;另外,我覺得還需要說明的是,現實生活中的對象并不是說其扮演了某個角色后才具有角色所定義的行為的,而是本來就有的,只不過是在扮演角色后表現出了該行為;所以對于現實生活中的對象,執行角色所定義的行為和扮演角色是同時發生的,沒有誰先誰后的說法;但是軟件中的對象則不同,因為軟件中的對象只是現實生活中的對象的某一個我們所關心的方面,所以它的能力也有限。另外從設計實現的角度職責單一的角度來說,我們也不會將軟件中的對象設計的很復雜,包含很多職責,因為這樣會導致對象難以維護,這樣做雖不違背分析原則,但違背設計原則。軟件中的對象我們往往是設計成當它扮演某個角色時,動態給對象注入角色所定義的交互行為,從而給對象賦予了參與場景交互的能力。因此簡單的說,軟件中的對象,平時只具有基本的狀態特征和基本的非交互行為,而當它扮演某個角色時,則動態具有交互行為;
- 分析交互過程結束后分別會對每個場景參與者對象產生哪些基本狀態特征的改變;這個很好理解,當一個對象參與了某個交互活動后,一些基本狀態特征會發生改變,比如一個人參加一次100米跑步比賽后,心跳速度會加快;心跳速度就是人的基本體征;這個應該很好理解吧,不多舉例了。
- 分析如何記錄和跟蹤這一次交互行為,分析這次交互行為會產生哪些額外的信息;這點估計大家平時很少思考,而這點我想也正是我的面向對象分析思路最具特色的地方吧!大家一定知道,一次對象的交互活動會產生一些與該交互活動相關的交互信息。比如一次應聘活動會產生一些與該活動相關的信息,如是否錄取,筆試成績,面試成績等;比如一次考試會產生考試成績這個信息;一次借書會產生一個借閱信息(包含:借書人、被借的書、借書時間,我們可能還會設計一個還書時間);并且,在很多情況下,這些交互信息會在后續的其他交互場景中再被更新。比如,一次應聘一開始狀態可能是”新投遞“,表示應聘人剛剛投了簡歷并選了某個職位,后來她去參加筆試或面試了,那么這次應聘的狀態就變為了”已筆試“或”已面試“;在比如一個學生參加一次考試,剛開始還沒有成績,但后來老師批卷子后便有了考試成績。再比如一次借書后,如果這本書還沒被還,則還書時間為空,而一旦還書了之后,便有了還書時間;因此,我們從這些規律中可以發現,交互其實是一個過程,并且該過程一旦開始后就會產生一些相關的信息,如應聘的狀態,考試的成績,借閱信息的歸還時間,等等。通常我們會把交互過程本身所涉及的一切信息以及交互過程所產生的所有附加信息作為一個整體來進行考慮。所以,我覺得我們有必要設計一個對象,用來表示某一次交互的結果,這個結果包含交互過程本身所涉及的一切信息以及交互過程所產生的所有附加信息;大家想想,看到”應聘“這個單詞,你有時候會認為它是一個動詞,優勢后會認為它是一個名詞。在認為是動詞時,我們關注的是交互本身,活動本身,強調行為;而在認為是名詞時,我們關注的是應聘行為所產生的一切信息;所以,看到這里,我想大家應該心里有個數了,就是在交互行為結束后,我們往往需要設計一個對象用來表示一次交互活動的相關信息;這些信息一方面體現了交互活動的參與者,(交互時間,交互地點,如果我們關心這些的話),另一方面體現了交互活動所產生的附加的信息,如成績,應聘狀態,借書還書時間,等等。最后,需要強調的是這些信息之所以設計為對象是因為這些信息不是歷史,即不是不可改變的,相反,這些信息會在后續的其他交互活動中被更新;所以,看到這里,我想我們就不用在糾結對象在參加交互活動后所產生的一些與它關聯的信息該如何存放這個問題了。
好了,上面就是我所掌握的面向對象分析思路。下面以圖書借閱系統(點擊這里下載源代碼)為例,按照上面的分析方法進行分析。
案例分析:圖書管理系統需求用例場景描述
圖書信息入庫場景
圖書館管理員掃描不同名稱的書本。大家都知道每本具體的書都有一個ISBN,即International Standard Book Number,國際標注書號。圖書館是根據ISBN來管理書本的,同一個ISBN的書會有很多不同的副本,每一個副本就是一本實實在在的書。所以,嚴格的說圖書館數據庫中保存的是“書本的庫存信息”。在圖書入庫的場景中,只要是ISBN相同,即書名相同的書只會被掃描一次,然后管理員會輸入這種書總共有多少本,應該放在什么地方等庫存信息。當然這只是我自己理解的業務場景,姑且不論對錯,咱們就先當這個理解是正確的吧。
借書場景
- 借書人持圖書卡去圖書館,先找到幾本要借的書,然后跑到借書處借書;
- 圖書管理員掃描借書人的借書卡以及書本的條形碼,條形碼就是ISBN;
- 借書人拿著書從圖書館離開;
還書場景
- 借書人吃圖書卡去圖書館,到還書處還書;
- 圖書管理員掃描還書人的借書卡以及書本的條形碼,如果有需要罰款的,也在此時會計算出來;
- 還書人離開圖書館;
結合面向對象分析思路和需求用例場景進行分析
圖書入庫場景的分析
場景參與者
圖書館、書本;其中圖書館在整個圖書借閱系統中只需要一個實例,我們也不關心是這個圖書館還是那個圖書館,所以我覺得可以把圖書館設計為一個服務。
參與者基本狀態特征
書本的基本狀態特征:如書名、作者、出版社等信息;
圖書館的基本狀態特征:沒有,一般情況下服務是無狀態的,服務只提供服務行為;
場景交互過程分析
很容易理解,圖書館本身就具有圖書入庫的行為。圖書入庫時,圖書館服務會生成并保存一個書本的“庫存信息”,該信息包含了某個名稱的書本的數量、書架位置信息。該庫存信息中的Count表示某個名稱的書有幾本。當在借書或還書的場景發生時,這個Count就會改變; 代碼示例如下:
圖書入庫場景類的實現:
{
private ILibraryService library = null;
private Book book = null;
public StoreBookContext(ILibraryService library, Book book)
{
this.library = library;
this.book = book;
}
public void Interaction(int count, string location)
{
library.StoreBook(book, count, location);
}
}
圖書館圖書入庫的方法:
{
//圖書入庫時生成圖書的庫存信息
var bookStoreInfo = new BookStoreInfo(book, count);
bookStoreInfo.Location = location;
bookStoreInfoRepository.Add(bookStoreInfo);
}
最后,交互之后,場景參與者的基本狀態特征是否發生變化?都沒有發生變化。
借書場景分析
場景參與者
圖書館注冊帳號、圖書館、書本;圖書館是一個服務、書本也只是一本普通的書,它沒有行為,它是被借的。需要著重分析的是圖書館注冊帳號這個參與者。先說一下我對:軟件使用者(即軟件的用戶)、注冊帳號、圖書卡、借書人這四個概念的理解。理解這些概念非常重要!首先,軟件使用者就是軟件的用戶,就是我們平時所說的用戶,這點沒什么問題。圖書卡和注冊帳號的關系是什么?我們都知道現實生活中,人持圖書卡去借書;而軟件中,人通過其注冊帳號登錄,然后以該注冊帳號所代表的“人”做事情;因此,圖書卡是用戶用來借書的工具;同樣注冊帳號也是軟件用戶通過軟件進行借書的工具;用戶擁有圖書卡,用戶擁有注冊帳號。那么借書人如何理解呢?我們平時所說的借書人其實就是一種角色,即用戶在通過借書卡參與借書的行為過程中,我們把正在執行該行為或已經執行了該行為的人叫做借書人。用更通用的表達方式就是,我們通常會給正在做某件事情或已經做了某件事情的參與者一個稱謂,如兇手、借書人、還書人、應聘人,等等;所以,有了這些理解之后,我們就知道借書人只是一個角色,某個注冊帳號扮演了借書人這個角色后可以行駛借書的行為;
參與者基本狀態特征
書本的基本狀態特征:如書名、作者、出版社等信息;
圖書館的基本狀態特征:沒有,一般情況下服務是無狀態的,服務只提供服務行為;
注冊帳號的基本狀態:圖書館注冊帳號有基本的狀態特征,比如:卡號,所有者姓名,是否鎖定,等等;
場景交互過程分析
某個軟件的使用者,即軟件用戶通過某個已注冊帳號登錄軟件系統,然后選擇了幾本想借的書之后點擊“借書”按鈕,然后該按鈕觸發一個借書的場景,該場景創建時包含了部分的場景參與者,在借書這個例子中就是帳號和書,然后借書場景知道帳號應該扮演借書者這個角色。所以帳號在扮演了借書者這個角色后對每一本書行駛借書的交互行為。扮演了借書者角色的注冊帳號自動具有了借書的行為,在該行為的內部實現中,通知圖書館把書借出來。隨后圖書館接到通知后,首先根據當前書本獲取書本的庫存信息,如果當前庫存信息是0本,說明這本書沒有庫存,就拋出異常通知軟件使用者這本書目前已經被借光了。如果還有庫存,則先更新庫存信息,比如圖書的數量減1,然后根據當前借書人,書本,以及當前時間生成一個借書信息對象,該對象還會包含一個圖書歸還時間的附加信息。最后將該借書信息對象保存起來。代碼示例如下:
借書場景類的實現:
{
private LibraryAccount account = null;
private IEnumerable<Book> books = null;
public BorrowBooksContext(LibraryAccount account, IEnumerable<Book> books)
{
this.account = account;
this.books = books;
}
public void Interaction()
{
var borrower = account.ActAs<IBorrower>();
foreach (var book in books)
{
borrower.BorrowBook(book);
}
}
}
借閱者的借書方法:
{
//通知圖書館把書借給我
library.LendBook(book, this);
}
圖書館借出書的方法:
{
//更新書本在圖書館的庫存信息,如:數量信息、所在書架位置信息
var bookStoreInfo = bookStoreInfoRepository.GetBookStoreInfo(book.Id);
if (bookStoreInfo.Count == 0)
{
throw new Exception(string.Format("The count of book '{0}' in library is zero, so you cannot borrow it.", book.BookName));
}
bookStoreInfo.DecreaseCount(); //數量減1
bookStoreInfo.Location = null; //位置清空
//生成借書信息并保存到Repository中
borrowInfoRepository.Add(new BorrowInfo(book, borrower, DateTime.Now));
}
最后,交互之后,場景參與者的基本狀態特征是否發生變化?都沒有發生變化。
還書場景分析
有了借書場景的分析,那么還書場景也很容易分析了,主要的過程是:注冊帳號扮演借閱者角色執行還書行為,執行過過程中通知圖書館接收某本要歸還的書,圖書館接到通知后首先調出與該書本對應的那一次借書信息,然后更新其還書時間,最后更新圖書的庫存信息。另外的基本和借書類似了,相信大家一看代碼都應該明白了,直接上代碼吧!
還書場景類:
{
private LibraryAccount account = null;
private IEnumerable<Book> books = null;
public ReturnBooksContext(LibraryAccount account, IEnumerable<Book> books)
{
this.account = account;
this.books = books;
}
public void Interaction()
{
var returnner = account.ActAs<IBorrower>();
foreach (var book in books)
{
returnner.ReturnBook(book);
}
}
}
借書者的還書方法:
{
//通知圖書館接收我要歸還的書
library.ReceiveReturnedBook(book, this);
}
圖書館接收被還的書的方法:
{
//設置借書信息的還書時間
var borrowedInfo = borrowInfoRepository.FindNotReturnedBorrowInfo(borrower.Id, book.Id);
borrowedInfo.ReturnTime = DateTime.Now;
//這里,真正的系統還會計算歸還時間是否超期,計算罰款之類的邏輯,因為我這個是一個演示的例子,所以不做這個處理了
//這里只更新書本的數量信息,因為還書時并不是馬上把書本放回書架的,所以此時書本的書架位置信息還是保留為空
//等到我們將這本書放到書架的某個位置時,才會更新其位置信息
var bookStoreInfo = bookStoreInfoRepository.GetBookStoreInfo(book.Id);
bookStoreInfo.IncreaseCount(); //數量加1
}
好了,差不多就這樣吧,從整理思考到文章寫作完成,花了我不少心思和時間那,希望大家都能看明白!另外一個題外話,大家在看我的源代碼時,不要過分注重我的框架實現,而應該注重這種面向對象的分析思路,框架是偏設計的,是用來為了更方便的支持由這種分析思路而產生的代碼實現。我想你懂的,呵呵!
浙公網安備 33010602011771號