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
的优点结合了起来。
关于作者