OO 設計過程:應用的用例,第 2 部分
完成用例
Allen Holub (allen@holub.com)
首席技術官,NetReliance
2001 年 5 月
來自IBM DW站點
本文是有關我編寫的 OO 設計過程的系列文章的繼續。前七個部分涵蓋了規劃階段,從初始設計到問題陳述的細化以及開始使用用例。在下個月轉向用戶界面以前,我將在本月結束用例的討論。
我在本月這篇文章中接著上個月繼續填寫“存款”用例的這個用例模板的剩余部分。和上個月一樣,我不僅填寫了模板,還提供了工作時思考過程的詳盡注釋。這個模板在本月的部分在某種程度上是用例描述真正的實質部分,因為它實際上描述了隨著用例進展的工作流。當用戶通過用例操作時,這個工作流就是程序在運行時所必須做的。
在我的上一篇專欄文章中,講述了大約一半的用例模板內容。我將繼續該話題。
填寫“存款”用例的用例模板
依賴性
類似于:2.0。小孩從帳戶取錢。(等同?)前一用例:4.0。家長開戶 包含:6.1。家長批準一筆存款 后一用例:6.0。家長批準較早的存款。
這個列表是暫定的。例如,用例 2.0 的“類似于”關系實際上可能是“等同”關系。
先決條件
帳戶必須存在。
輸入
小孩掙得了一些錢。
方案
(“一路順風”)
Philip 干完了當天的家務后決定將掙得的 2.25 美元存到 Allen 銀行。(如果他每天做家務的話,我現在可能就破產了。)他進入銀行,向出納員要一張存款單,填完存款單后把存款單和他的存折一起交給出納員。出納員在接受存款單之前要求某種身份驗證,然后將存款單交給銀行主管人批準。銀行主管人(家長)在存款單簽名批準存款,然后把它交還給出納員,出納員再更新 Philip 的存折以顯示此次交易。然后出納員將存折還給 Philip,他便可以愉快地估算自己正在增長的存款余額了。
請注意在這個方案描述中,我小心謹慎地使用了問題陳述(在本系列文章中的前面的部分中討論過)的詞匯(例如,“存折”、“出納員”等等)。只要我在這里引入了新的概念 — 那種情況確實會發生 — 我就必須回過頭修改問題陳述來引入新的詞匯。設計者的一個重要的(也是困難的)目標就是使所有設計文檔能相互保持“協調”。如果一個文檔中的更改影響到另一個文檔,那么這兩個文檔都必須立即更改。例如,我有意不說“出納員制作一個反映儲蓄交易的日志項”,因為“日志”不屬于這個領域的詞匯而“存折”屬于其中。對于設計而言,把它稱做“日志”或“存折”沒有區別。
還要注意我沒有使用“系統”一詞!跋到y”不做任何事情;所有的工作都由扮演著“精心設計的”角色的參與者完成。某些參與者(小孩)是真人。另一些參與者(出納員)是完全自動化的。還有一些參與者(銀行主管人)在有些情況下是自動化的,在有些情況下是真人(銀行主管人的某些職責由家長而不是計算機執行)。重要的是“角色”。至于扮演“角色”的參與者是不是自動化的并不是問題的實質。
在這個方案里,錢實際上沒有加到帳戶里,更準確地說,這筆存款是以“未批準”的狀態輸入存折的。任何家長可以在隨后的時間里批準該交易。換句話說,存款在批準以前處于“扣留”狀態。出納員將存折還給小孩,并且在存折上會反映出這筆存款,但是在家長在存款單上簽字以前這筆錢不能從帳戶取出。(這個方案與“一路順風”不同之處在于沒有家長來批準存款。)
在這個方案里,家長為小孩存錢(不同于由小孩開始然后家長批準的交易)。
在這一時刻,和其它不太冒失的詞相比,我覺得“小孩”這個詞相當不妥。我之所以還用小孩這個詞 — 而不是用孩子、客戶或其它等價的術語 — 是因為在整個設計文檔集中保持命名的內部一致性是很重要的。但我還是希望我選擇術語時能更謹慎一點。
第二個方案實際上引入了一個新的用例,這是我以前沒有意識到的:“家長批準優先于存款”。這確實是一個獨立的用例,即使批準在當前用例中也是一個操作(意味著一個子用例:家長批準一筆存款)。
工作流
這部分是一個關鍵,所以我將使用與到目前為止我一直使用的略有不同的格式并且顯示工作流是如何發展的。首先,我將“一路順風”作為一個簡單列表建模。
“一路順風”方案:
小孩從出納員領取存款單并填寫。
小孩將存款單和存折一起提交給出納員。
出納員驗證小孩的身份是否有效。
出納員將存款單交給銀行主管人以獲得批準。
銀行主管人將存款單返回給出納員,標記為批準。
出納員在小孩的存折里寫一條記錄以顯示這筆存款。
出納員將存折交還給小孩。
盡管對于簡單的工作流線性列表非常有效,但當工作流復雜的時候它就不能滿足要求了。
Constantine 和 Lockwood 建議,表示非線性操作時使用象“以任何次序:”后面跟一個列表。還可以用“同時地”后面跟一個表示并發的列表。
可能我是那種喜歡形象化的人吧,我發現這些僅用文字表示用例會使人糊涂。當用例很復雜時我更喜歡 UML 工作流程圖!耙宦讽橈L”的 UML 如圖 1 所示。
圖 1. “一路順風”工作流程圖
符號差不多是自解釋的。每個框表示一個活動,箭頭表示工作流。從實心圓開始并以空心圓結束。這些列,被稱為“泳道(swim lane)”,標識負責執行活動的對象(或子系統)的類。
在看圖時,我注意到有些地方可以有并行性。即,某些活動(驗證小孩的身份和批準存款)可以并行完成。(您也許會發現,倘若在進行到下一步之前這些活動都被執行了的話,則它們執行的順序并不重要。)
圖 2 顯示了為反映并行性而對圖 1 所做的修改。表示一組并行行為開始的粗水平線(在表示這組行為的流程圖的頂部)稱為“分叉”(fork)。底部的線(表示到達這點時,這些并行行為在繼續后一步可能的工作之前必須要同步)稱為“匯合”(join)。
圖 2. 經過修改的“一路順風”以反映并行性
現在我回過頭考慮另外兩個方案,并試著把它們合并到“一路順風”圖中。(修改后的圖見圖 3。)通常,如果不可能合并這些方案或合并后生成的圖顯得非常笨拙,那么這些附加的方案可能應成為獨立的用例,而不是當前用例的方案。當然,目前這幾個方案合并得很好。
在我意識到倘若我允許家長扮做小孩的角色那么工作流就和“一路順風”一模一樣以后,解決家長為小孩存款這一方案就不費吹灰之力了。就是說家長的密碼可以用來登錄小孩的帳戶。我將這一結果的注釋放在活動圖(圖 3)和商業規則部分(在下面)。
第二個方案(家長不在場)稍稍有些困難,因為我必須引入分支(branch)(在圖 3 中以有一個輸入箭頭和多個輸出箭頭的菱形表示)?刂蒲啬膫輸出路徑而行的條件(稱為“監護”(guard))就是在線上的文字標簽。合并(merge)(也由一個菱形表示,但有多個輸入箭頭和一個輸出箭頭)標記條件行為的終止。每個分支應該有對應的合并。
可以如下表達在“分叉”與“匯合”之間的所有復雜的情況,如下:驗證小孩的身份并在家長在場的情況下標記存款為批準,或者當家長不在場時標記為未批準(這兩個過程不分先后次序)。當兩個活動(驗證和標記)都完成后就更新存折。
請注意,最初的方案描述說明出納員扣留未批準的存款。當使用這個圖時,我們可以明顯地看到將未批準的存款扣留在存折中比由出納員扣留方便得多,于是我就那樣做了。我回過頭修改了方案描述以反映這種更改。
圖 3. 將附加方案添加到“一路順風”
后置條件
新帳戶余額 = 舊帳戶余額 + 存款額
請注意這個用例包含了兩個不必同時發生的不同操作(存款請求和存款批準)。然而,這的確是一個具有單個結果的單個用例。帳戶余額總會改變,即使存款活動與批準活動之間相隔數日。
輸出
更新的存折。
這一部分將是顯示輸出形式的地方(例如,以報表的形式)。我將這一部分推遲到隨后文章中開始解決 UI 的時候再講述。
商業規則(與領域相關)
存款生效前必須由家長批準。
利息自存款當日計算,即使幾天之后才有批準。
未批準存款能無限地處于未批準狀態,等待家長簽名。它們不會“過期”。
家長的密碼將驗證小孩。即家長可以作為小孩登錄。
需求(與實現相關)
存款必須自動“保存”。這里沒有“File/Save”菜單。顯式地“撤銷”沒有必要也不需要?梢杂萌】顏沓蜂N存款。
當小孩看存折時,必須顯示“扣留”的存款。以灰色顯示扣留的存款。
如果我說的是“以某種方式突出顯示”而不是“以灰色顯示”,那我將冒險寫下無意義的規范。到結束時,規范必須明確。既然最后都要做出決定,那么不妨現在就開始。如果做了錯誤的決定那么總是可以回過頭去做更改(當然是在規范正式批準以前)。
對我來說做錯誤的決定總比不做決定要好。我看到過的許多所謂的規范都太不直接明了,以至于可以從這個規范生成幾百個不同的程序,而且所有的程序都是符合這個規范的。這種不明確性比無效更糟,因為它促使您做一些不正確的工作。
在當前用例中,我想這個問題顯得太瑣碎,以至不必作決定。只要對這一項做了突出顯示,至于如何做就并不重要了。因而這個決定可以推遲到正式的 UI 設計過程。如果突出顯示的概念模糊不清,那么實現設計的各個程序員將按各自對突出顯示這一概念的理解實現,導致 UI 不一致,其結果是更難使用。
實現注解
目前還沒有登錄的概念?粗@些方案,Philip 不大會一次做幾項交易。事實上,更有可能是他只做一項交易,但是忘了注銷。(這是我在家里觀察到的行為 — Philip 經常在早上玩游戲機,而我會發現游戲機在 12 小時之后還開著,也就是在他上床睡覺之后。)考慮到這種行為,接受存款單的同時還要求某種驗證似乎更安全些。這也是真正的銀行的本來的運作方式。
與登錄類似的是在被允許進入銀行之前向警衛表明身份。(那也許是一個合理的方法,只不過我沒有選它。)請注意在這個用例中沒有禁止出納員記住 Philip 是誰。也就是說,客戶在這個用例的作用域內以某種方式被標以“可信的”,這樣不需要重復驗證過程就可以執行隨后的交易!翱尚诺摹睂傩钥梢酝ㄟ^注銷來取消,或者干脆在給定時間到了以后,取消這個屬性。(例如,如果在銀行您兩分鐘后仍無任何活動的話,將變成不可信的。)
(在我們開始真正的對象建模之前,您可能會不明白這個注解,所以如果現在不理解,不必擔心。)在大多數情況下,小孩是外部對象。也就是說,系統里沒有程序性的對象表示小孩;更準確的說,小孩是系統的物理用戶。另一方面,既然系統里某個對象必須處理驗證,那么小孩身份的概念幾乎肯定會被建模。
“可用資金”與“當前余額”之間有所不同。(后者不反映未批準交易。)既然小孩認為存款就是存款,不管是否批準,存折應該把二者的值都顯示出來。不管怎樣,告訴小孩“扣留”的工作原理是沒有壞處的。
我還沒確定怎樣在 UI 表示扣留才最好。解決方案之一是在存款單上向查看存折的小孩顯示扣留,但在存款單獲得家長批準之后存款才會在存折上正確地顯示。另一個可能的方案是存款象獲得批準的存款一樣顯示在存折上,但帶有表示存款正被扣留的某種可視化標記(灰色文字?)。
后者更好一些,但這意味著“家長批準當前存款”的用例將可能與“家長批準暫掛的存款”的用例有明顯的不同。
請注意,如果出納員為家長批準而扣留存款單,小孩將不得不請出納員看存折,以便于出納員可以顯示扣留的存款單和存折。
解決家長批準的最好方法可能就是 UI 上的存款單本身;蛟S有一個“批準的”框,在選中后會彈出一個小的對話框用來輸入簽名。另一個方法是在存款單上有一個“批準簽名”欄,家長可以在其中輸入“同意”這類短語。(請注意交易已批準的指示 — 例如,簽名的副本 — 應該出現在經過批準的存款單上)。請不要在“經批準的簽名”欄中顯示 x(或至少做到:一旦簽名被驗證就替換掉這些 x);在通過驗證的階段泄露字符的數目實在沒有必要。
這里有一些訪問控制問題。扮演小孩角色的參與者能指定存款,但不能批準存款。(批準會使帳戶余額和存折被更新。) 扮演家長角色的參與者既能指定存款也能批準存款。系統必須能夠區分這兩種角色的參與者 — 密碼驗證就足夠了。
扣留資金的利息會被累加,即使還不能動用這些錢。處理利息可能的最好辦法是把存折看作交易的列表,按日期排序。當存款單被批準后,將把它按已排的順序插入到交易列表中。利息將從最近插入的交易的時刻開始重新計算。若用這種方法,則未批準的交易實際上沒有生利息。
然而您也許想根據天數來顯示利息的增長,那么象真正的銀行存折或銀行帳單那樣只列出交易就顯得不夠。您實際需要每天有一行項目。但是會有很多行。一個合理的折衷辦法是在每天計息的基礎上顯示前 30 天的信息,而存折剩余部分則象標準的銀行帳單一樣按常規顯示公布的利息(不是每天),可能每星期公布一次。
這就是完整的用例介紹。為了不使您感到厭煩,我不在隨后的文章里介紹用例的剩余部分,我將把設置帳單的整個過程放到我的網站上(請參閱參考資料)以便您在空余時間瀏覽。下個月我將繼續這個設計過程:研究 UI 設計(它是必不可少的,不僅因為我們將必須構建它,還因為 UI 能用于驗證這些用例和提供有用的通信工具)。
參考資料
本文是有關我編寫的 OO 設計過程系列文章的繼續。前七個部分是:
入門
開始設計軟件
細化問題定義
驗證分析
用例,簡介
用例規劃
應用的用例,第 1 部分
可以在 http://www.holub.com/ 訪問 Allen 的網站。
Constantine 和 Lockwood 所建議的表現非線性操作的方法與本文中的不同。
在 Allen Holub 的什么是對象?(JavaWorld:July,1999)中討論了由對象的操作定義對象的概念。這個系列的后續文章(在 http://www.holub.com/aiharticles.html 上有所有文章的索引)討論的是如何將這種原理應用到現實世界的系統。
在 Martin Fowler 和 Kendall Scott 合著的 UML Distilled, 2nd Ed 一書中很好地描述了工作流程圖(連同剩下的 UML)。
文章來源于領測軟件測試網 http://www.kjueaiud.com/
版權所有(C) 2003-2010 TestAge(領測軟件測試網)|領測國際科技(北京)有限公司|軟件測試工程師培訓網 All Rights Reserved
北京市海淀區中關村南大街9號北京理工科技大廈1402室 京ICP備10010545號-5
技術支持和業務聯系:info@testage.com.cn 電話:010-51297073
老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月