Granville 繼續討論“統一建模語言”和序列圖的繪制。他仔細研究了序列圖繪制過程中條件邏輯的角色,并討論了為什么要在圖中包含或排除條件和循環。Granville 還描述了序列圖的兩種形態 -- 常規和實例 -- 并說明了它們在開發周期中各自的應用。
我在
介紹性專欄中曾經解釋過,序列圖用于描述系統隨時間而產生的內部行為。因為系統行為是對象相互之間發送消息的結果,因此序列圖繪制了那些消息在對象之間移動時的路線。
歸根結底,序列圖就是交互圖。在前一部分中,盡管我們描述了無數交互,但只創建了一個相當簡單的圖。這次,我們將做進一步的研究,看看
UML
指定的序列圖的兩種形態。這兩種形態是
常規和
實例。讓我們從每種形態的正確應用開始。
序列圖的兩種類型
序列圖用于描述對象之間兩種不同類型的交互。一種交互類型是
必須
(must) 交互,其中對象 A 必須向對象 B
發送特定消息。另一種交互類型是
可能 (may) 交互,其中對象 A
可能(但不一定)向對象 B
發送特定消息。這兩種形態的序列圖描述了這兩種不同類型的交互。常規形態描述的是
必須交互,而實例形態則描述了
可能交互。
常規形態的序列圖描述初始刺激因素所產生的類交互。常規形態則記述了初始刺激因素能夠產生的一切交互。成功和失敗條件與循環、條件和分支一樣,都是這種圖的組成部分。
常規序列圖在水平軸方向上的每個框中只包含一個類名,如圖 1
所示。它的含義是,交互背后的對象是匿名的,該類的任何對象都可以參與到交互中。因此,必須為所有路徑明確建模。在常規序列圖中,對象
A 必須向對象 B 發送模型中的一條消息。
圖 1. 常規序列圖
序列圖的第二種形態是實例形態。實例序列圖描述了兩個實例之間可能發生的單一消息交換。這樣的圖將在水平軸方向的框中包含一個變量名及其類類型,如圖
2
所示。這種形態不包括常規形態中常見的循環、條件和分支。在系統中實際的控制流程中,在交互過程中所進行的某些斷言可能為假。如果發現斷言為假,實例序列圖中的所有消息都為空,這種情形將不出現。實例序列圖描述了可能發生也可能不發生的單一情形。
圖 2. 實例序列圖
實例序列圖最適合于在軟件開發生命周期的分析階段對個別方案建模。常規序列圖可以為包含多個方案的整個用例建模。其它一些類型的活動
-- 例如為子系統或框架與其各個部分之間使用的協議建模 --
可以使用任何一種形態,這取決于組件在軟件開發生命周期中所處的位置。與實例形態相比,常規形態更接近于在最終產品中出現的實際代碼。
我們在前一專欄中使用的是常規形態,并將在此繼續研究這種形態。這一次,我們將探究條件邏輯在常規序列圖中所扮演的角色,通過它來讓您了解有關
UML 表示的更多知識。
序列圖繪制中的條件邏輯
常
規序列圖利用了條件邏輯,這對于描述交互過程中事件的可選流程來說很有用處。根據軟件開發生命周期中所處的不同階段,可以繪制詳略度不同的圖。在分析階
段,您可能愿意將詳細信息排除在條件表達式以外,而在設計階段,您卻可能希望將最終產品中要使用的代碼的片段包括在條件表達式中。
無論處于開發周期中的哪個階段,隨著條件表達式圖的繪制,序列圖與如
Java
語言這樣的面向對象語言之間那種自然的一致性就愈發清楚了。例如,請考慮一個允許出納員接受存款的銀行業務應用。除了其它一些事項以外,還規定了系統必須防止出納員把負的金額記入帳戶貸方,因為這會導致從帳戶中扣除。因此系統必須有一種檢查鍵入的所有金額均為正數的機制。
清單 1 顯示了確保存款為正數的條件表達式。
清單 1.
帶有條件表達式的方法
\** This is a method in a Teller class **\ public void receiveDeposit(Account account, BigDecimal deposit) throws ImproperDepositException { // Check to ensure the deposit is positive if (deposit.compareTo(new BigDecimal(0.0)) > 0) { account.credit(deposit); } else { throw new ImproperDepositException(); } }
|
在分析階段,您不是很關心細節,因此圖只需要表明存款為正數。在常規序列圖中,條件作為帶有消息名的保護機制出現,位于水平調用箭頭上方。這些保護條件用方括號括起,放在消息名的左側,如圖
3 所示。
圖 3.
在分析期間添加的條件
上述方法和序列圖之間的關系在圖 4 中顯現得更為清楚,我們在圖 4
中看到了在設計階段可能用到的更明確的圖。當然沒有顯示全部方法:缺少了
else
子句。不過,圖中消息箭頭的語義規定只能在條件有效時發送消息。
圖 4. 更明確的條件
可以通過在
Teller
類和
ImproperDepositException
之間添加另一個調用箭頭來為
else 子句建模。在這個調用上會有一個與 if
相反的條件;在本例中,即存款必須小于等于
0。您不妨自己嘗試為這個語句建模。
繪制
for 循環圖
如上例所示,常規序列圖 -- 以及實際上所有 UML 圖 -- 幾乎映射了 Java
語言的語法。所以,大多數 Java
開發者對這些圖都有一個直觀的理解,并且可以很快地學會如何使用它們。為進一步探討常規序列圖和
Java 語言之間的一致性,我們將繪制
for
循環圖,如清單 2
所示。
清單 2.
for
循環
for ( int i =0; i < 4; i++) { squareRoom.examineCorner(i); }
|
在序列圖中,迭代是通過水平箭頭上消息名之前的星號 (*)
來表示的。如果迭代的次數已知并且固定 -- 這種情況非常少見 --
這個數字出現在星號后面的方括號中。因為大多數
for
循環處理的復雜邏輯不允許靜態地確定迭代次數,因此您不會經常使用這種格式的括號表示。圖
5 顯示了上述
for
循環的序列圖。
圖 5. for
循環序列圖
繪制
while 循環圖
因為
while
循環將循環與條件結合起來,因此它是個非常容易接受的示例。我們將對清單
3 中顯示的
while
循環繪制圖。
清單 3. while 循環
while ( value.notFound() ) { value = database.search( key ); }
|
我們的
while
循環圖既包含條件,又包含表明迭代的星號,但您會發現,沒有迭代的次數。
while
循環很少包含迭代次數 -- 除非它是一個偽裝的
for
循環。圖 6 顯示了
while
循環圖。
圖 6. while
循環序列圖
結束語
一般來說,
必須和
可能行為是 UML
和軟件開發的基本概念。用例捕捉
必須行為;方案捕捉
可能行為。
類圖捕捉
必須行為;實例圖捕捉
可能行為。
我主要討論這一概念是因為我發現許多人沒能掌握序列圖的根本靈活性,而分化成直覺和形態使用這兩個極端。
在閱讀這些文章時,您應該把精力集中在發展模型語義的直觀理解上。
隨著看到越來越多的序列圖并開始創建自己的序列圖,您會發現許多序列圖依賴于條件邏輯和圖表上下文來說明圖所表示的是
必須還是
可能的系統視圖。隨著我們深入到更加復雜的圖表繪制技術,及早學習如何識別和使用這種差別將對您今后有所幫助。
除了探討序列圖繪制中
必須和
可能行為的重要性以外,我還向您介紹了如何在圖中表示條件和迭代。既然您已經知道如何繪制
for
和
while
循環圖,我建議您在其它 Java
構造(例如
do-while
循環)上實踐一下建模表示。隨著您自己練習繪制這些簡單構造圖,自然會逐漸加深對序列圖繪制的理解。
參考資料
- 您可以參閱本文在 developerWorks 全球站點上的
英文原文.
- 通過單擊本文頂部或底部的
討論來參與有關該文章的
討論論壇。
- 有關“統一建模語言”和序列圖繪制的介紹,請參閱
本系列的第一個專欄。
- 要了解有關 UML 和序列圖繪制的詳細信息,請仔細查看 Hans-Erik
Eriksson 和 Magnus Penker 的
UML
Toolkit
(John Wiley & Sons, 1997)。
- 另一個非常有幫助的資源是
UML in a Nutshell
,由 Sinan Si Alhir 著 (O'Reilly &
Associates, 1998)。
- “UML 標準”的權威來源是 OMG 的
統一建模語言規范
(Unified Modeling Language Specification)(在本文寫作時是版本
1.4)。
- 有關序列圖和 UML 的其它信息,請參閱 "
建立帶有樣式的
UML 序列圖",由 Scott W. Ambler 著(developerWorks,2001 年 2
月)。
- Allen Holub 在他有關面向對象設計過程的系列中提供了
用例方案的深入說明(developerWorks,2001 年 1 月)。
- 閱讀
OCL,它是 UML 的表達式語言。
- IBM 和其它一些業界領先者創建了
XMI,一種新的開放業界標準,它將一些基于 Web
的用于定義、確認和共享文檔格式等 XML 標準的優點和 UML
的優點結合了起來。
關于作者