1、UML 序列图编写规范一、序列图简介序列图主要用于按照交互发生的一系列顺序,显示对象之间的这些交互。很象类图,开发者一般认为序列图只对他们有意义。然而,一个组织的业务人员会发现,序列图显示不同的业务对象如何交互,对于交流当前业务如何进行很有用。除记录组织的当前事件外,一个业务级的序列图能被当作一个需求文件使用,为实现一个未来系统传递需求。在项目的需求阶段,分析师能通过提供一个更加正式层次的表达,把用例带入下一层次。那种情况下,用例常常被细化为一个或者更多的序列图。组织的技术人员能发现,序列图在记录一个未来系统的行为应该如何表现中,非常有用。在设计阶段,架构师和开发者能使用图,挖掘出系统对象间的
2、交互,这样充实整个系统设计。序列图的主要用途之一,是把用例表达的需求,转化为进一步、更加正式层次的精细表达。用例常常被细化为一个或者更多的序列图。序列图除了在设计新系统方面的用途外,它们还能用来记录一个存在系统(称它为“遗产”)的对象现在如何交互。当把这个系统移交给另一个人或组织时,这个文档很有用。二、序列图元素2 1 框架 在 UML 2 中,框架元件用于作为许多其他的图元件的一个基础,但是大多数人第一次接触框架元件的情况,是作为图的图形化边界。当为图提供图形化边界时,一个框架元件为图的标签提供一致的位置。在 UML 图中框架元件是可选择的;就如你能在图 1 和 2 中见到的,图的标签被放在
3、左上角,在我将调用框架的“namebox” 中,一种卷角长方形,而且实际的 UML 图在较大的封闭长方形内部定义。图 1: 空的 UML 2 框架元件2 2 图类型UML 规范给图类型提供特定的文本值。(举例来说, sd 代表序列图,activity 代表活动图,use case 代表用例图)。2 3 生命线画一个序列图的时候,放置生命线符号元件,横跨图的顶部。生命线表示序列中,建模的角色或对象实例。 1 生命线画作一个方格,一条虚线从上而下,通过底部边界的中心(图 3)。生命线名字放置在方格里。图 3: 用于一个实体名为 freshman 的生命线的 Student 类的一个例子24 消息为
4、了可读性,序列图的第一个消息总是从顶端开始,并且一般位于图的左边。然后继发的消息加入图中,稍微比前面的消息低些。为了显示一个对象(例如,生命线)传递一个消息给另外一个对象,你画一条线指向接收对象,包括一个实心箭头(如果是一个同步调用操作)或一个棍形箭头(如果是一个异步讯号)。消息/方法名字放置在带箭头的线上面。正在被传递给接收对象的消息,表示接收对象的类实现的一个操作/方法。在图 4 的例子中,analyst 对象调用 ReportingSystem 类的一个实例的系统对象。analyst 对象在调用系统对象的 getAvailableReports 方法。系统对象然后调用secSystem
5、对象上的、包括参数 userId 的 getSecurityClearance 方法,secSystem的类的类型是 SecuritySystem。 2图 4: 一个在对象之间传递消息的实例2 5 约束当为对象的交互建模时,有时候,必须满足一个条件,消息才会传递给对象。约束在 UML 图各处中,用于控制流。在这里,我将会讨论 UML 1.x 及UML 2.0 两者的约束。在 UML 1.x 中,一个约束只可能被分配到一个单一消息。UML 1.x 中,为了在一个序列图上画一个约束,你把约束元件放在约束的消息线上,消息名字之前。图 5 显示序列图的一个片段,消息 addStudent 方法上有一个
6、约束。图 5:UML 1.x 序列图的一个片段,其中 addStudent 消息有一个约束在图 5 中,约束是文本“ pastDueBalance=0” 。通过这个消息上的约束,如果应收帐系统返回一个零点的逾期平衡,addStudent 消息才将会被传递。约束的符号很简单;格式是:Boolean Test举例来说,pastDueBalance = 026 组合碎片(变体方案,选择项,和循环)然而,在大多数的序列图中,UML 1.x“in-line”约束不足以处理一个建模序列的必需逻辑。这个功能缺失是 UML 1.x 的一个问题。 UML 2 已经通过去掉“in-line”约束,增加一个叫做组合
7、碎片的符号元件,解决了这一个问题。一个组合碎片用来把一套消息组合在一起,在一个序列图中显示条件分支。UML 2 规范指明了组合碎片的 11 种交互类型。十一种中的三种将会在“基础” 段落中介绍,另外两种类型将会在“超越基础” 中介绍,而那剩余的六种我将会留在另一篇文章中介绍。(嗨,这是一篇文章而不是一本书。我希望你在一天中看完这部分!)2 7 变体变体用来指明在两个或更多的消息序列之间的、互斥的选择。 3 变体支持经典的“if then else”逻辑的建模(举例来说,如果 我买三个,然后 我得到 我购买的 20% 折扣;否则 我得到我购买的 10% 折扣)。就如你将会在图 6 中注意到的,一
8、个变体的组合碎片元件使用框架来画。单词“alt”放置在框架的 namebox 里。然后较大的长方形分为 UML 2 所称的操作元。 4 操作元被虚线分开。每个操作元有一个约束进行测试,而这个约束被放置在生命线顶端的操作元的左上部。 5 如果操作元的约束等于“true”,然后那个操作元是要执行的操作元。图 6:包含变体组合碎片的一个序列图片段图 6 作为一个变体的组合碎片如何阅读的例子,显示序列从顶部开始,即bank 对象获取支票金额和帐户结余。此时,序列图中的变体组合碎片接管。因为约束“balance = amount” ,如果余额超过或等于金额,然后顺序进行 bank对象传递 addDebi
9、tTransaction 和 storePhotoOfCheck 消息给 account 对象。然而,如果余额不是超过或等于金额,然后顺序的过程就是 bank 传递addInsuffientFundFee 和 noteReturnedCheck 消息给 account 对象,returnCheck 消息给它自身。因为“else” 约束,当余额不大于或者等于金额时,第二个序列被调用。在变体的组合碎片中,不需要“else”约束;而如果一个操作元,在它上面没有一个明确的约束,那么将假定“else”约束。变体的组合碎片没被限制在简单的“if then else”验证。可能需要大量的变体路径。 如果需要
10、较多的变体方案,你一定要做的全部工作就是把一个操作元加入有序列约束和消息的长方形中。 28 选择项选择项组合碎片用来为序列建模,这些序列给予一个特定条件,将会发生的;或者,序列不发生。一个选择项用来为简单的“if then”表达式建模。(例如,如果架上的圈饼少于五个,那么另外做两打圈饼)。选择项组合碎片符号与变体组合碎片类似,除了它只有一个操作元并且永不能有“else”约束以外(它就是如此,没有理由)。要画选择项组合,你画一个框架。文字“opt”是被放置在框架的 namebox 里的文本,在框架的内容区,选择项的约束被放置在生命线顶端上的左上角。 然后选择项的消息序列被放在框架的内容区的其余位
11、置内。图 9:包括选择项组合碎片的一个序列图片段阅读选择项组合碎片很容易。图 9 是图 5 的序列图片段的再加工,但是这次它使用一个选择项组合碎片,因为如果 Student 的逾期平衡等于 0,需要传递更多的消息。按照图 9 的序列图,如果 Student 的逾期平衡等于零,然后传递 addStudent,getCostOfClass 和 chargeForClass 消息。如果 Student 的逾期平衡不等于零,那么在选择项组合碎片中,序列不传递任何一个消息。2 9 循环有时候你将会需要为一个重复的序列建模。在 UML 2 中,为一个重复的序列建模已经改良,附加了循环组合碎片。循环组合碎片
12、表面非常类似选择项组合碎片。你画一个框架,在框架的 namebox 中放置文本“loop”。在框架的内容区中,一个生命线的顶部,循环约束 被放置在左上角。然后循环的消息序列被放在框架内容区的其余部分中。在一个循环中,除了标准的布尔测试外,一个约束能测试二个特定的条件式。特定的约束条件式是写作“minint = the number”(例如,“minint = 1”)的最小循环次数,和写作“maxint = the number”(例如,“maxint = 5”)的最大循环次数。通过最小循环检验,循环必须运行至少指定次数,而循环执行次数不能达到约束指定的最大循环次数。2 10 超越基础我已经介绍
13、了序列图的基础,应该使你可以为将会在系统中通常发生的大部份交互建模。下面段落将会介绍用于序列图的比较高阶的符号元件。 2101 引用另外一个序列图当做序列图的时候,开发者爱在他们的序列图中,重用存在的序列图。 7 在 UML 2 中开始,引进“ 交互进行” 元件。追加交互进行的可以说是 UML 2 交互建模中的最重要的创新。交互进行增加了功能,把原始的序列图组织成为复杂的序列图。由于这些,你能组合(重用)较简单的序列,生成比较复杂的序列。这意味你能把完整的、可能比较复杂的序列,抽象为一个单一的概念单位。2102 门有另一个方法在序列图之间传递消息。门可能是一个容易的方法,为在序列图和它的上下文
14、之间的传递消息建模。一个门只是一个消息,图形表示为一端连接序列图的框架边缘,另一端连接到生命线。使用门的图 11 和 12 ,在图 14 和 15 中可以被看到重构。图 15 的例图有一个叫做 getBalance 的入口门,获取参数 accountNumber。因为是箭头的线连接到图的框架,而箭头连接到生命线,所以 getBalance 消息是一个入口门。序列图也有一个出囗门,返回balance 变量。出口门同理可知,因为它是一个返回消息,连接从一个生命线到图的框架,箭头连接框架。图 6 图 12 的重构,这次使用门2103 组合碎片(跳转和并行)在本文前面“ 基础” 的段落中呈现的,我介绍
15、了“变体”,“ 选择项”,和“ 循环”的组合碎片。这些三个组合碎片是大多数人将会使用最多的。然而,有二个其他的组合碎片,大量共享的人将会发现有用跳转和并行。1跳转跳转组合碎片几乎在每个方面都和选择项组合碎片一致,除了两个例外。首先,跳转的框架 namebox 的文本“break” 代替了“option”。其次, 当一个跳转组合碎片的消息运行时,封闭的交互作用的其他消息将不会执行,因为序列打破了封闭的交互。这样,跳转组合碎片非常象 C+ 或 Java 的编程语言中的break 关键字。2并行今天的现代计算机系统在复杂性和有时执行并发任务方面不断进步。当完成一个复杂任务需要的处理时间比希望的长的时
16、候,一些系统采用并行处理进程的各部分。当创造一个序列图,显示并行处理活动的时候,需要使用并行组合碎片元件。并行组合碎片使用一个框架来画,你把文本“par”放在框架的 namebox 中。然后你把框架的内容段用虚线分为水平操作元。框架的每个操作元表示一个在并行运行的线程。图 7: oven 是并行做两个任务的对象实例图 17 可能没有举例说明做并行活动的对象的最好的计算机系统实例,不过提供了一个容易理解的并行活动序列的例子。序列如这样进行:hungryPerson 传递 cookFood 消息给 oven 对象。当 oven 对象接收那个消息时,它同时发送两个消息(nukeFood 和 rota
17、teFood)给它本身。这些消息都处理后,hungryPerson 对象从 oven 对象返回 yummyFood 。三、总结序列图是一个用来记录系统需求,和整理系统设计的好图。序列图是如此好用的理由是,因为它按照交互发生的时间顺序,显示了系统中对象间的交互逻辑。脚注1 在完全建模系统中,对象(类的实例)也将会在系统的类图中建模。2 当阅读这个序列图时,假定分析师登录进入系统之内。3 请注意,附着在不同的变体操作元上的、两个或更多的约束条件式的确可能同时是真,但是实际最多只有一个操作元将会在运行时发生(那种情况下变体的“wins”没有按照 UML 标准定义) 。4 虽然操作元看起来非常象公路上的小路,但是我特别不叫它们小路。泳道是在活动图上使用的 UML 符号。请参考 The Rational Edge 早期关于 活动图的文章。5 通常,附上约束的生命线是拥有包含在约束表达式中的变量的生命线。6 关于选择项组合碎片,循环组合碎片不需要在它上放置一个约束条件。7 可能重用任何类型的序列图(举例来说,程序或业务)。我只是发现开发者更喜欢按功能分解他们的图。