第四章 时序图
一般来说,我们会在一张时序图中画出某个情节的相关行为,图种会秀出这个使用案例(use case)里面可能出现的一些对象,以及在对象间传送的信息。
本章将通过一个简单情节,做时序图各方面的相关讨论。假设我们现在有一份订单,并且准备调用它的一个命令,算出这份订单的价格。为了达到这个目的,订单需要产看它里面所拥有的一些订单明细、决定它们的价格,价格决定方式是以订单明细中所包含产品之定价规则为基础决定的。对所有订单明细做完上述动作之后,接下来订单要算出整个折扣,这时候它是以跟客户绑在一起的规则为基础算出真个折扣的。
下图是这个情节的一种实现方式。时序图画出互动情形的方式是让每个参与对象有一条生命线,信息会由页面上方往下依序执行,而且讯息的先后顺序也是页面往下看的。
说明:对图中的参与对象来说,光是看一些命名方式就可以把它们联系起来。从图中,我们可以看到getProduct的呼叫会传回aProduct,它跟接下来getProcingDetails呼叫时所传送的目的地aProduct名称一样,因此两者值得是同一个参与对象。请注意,我只有提这个呼叫画出返回消息箭头;之所以这么做是为了秀出者两个呼叫间的相关性。
对时序图来说,它的好处是:我几乎不用解释这个表示法,大家就能够了解它。
下图是这个情节的另一个实现方式。他解决的问题还是一样,不过这些参与对象一起合作,已解决问题的方式却非常不同。Order要求每个Order Line算出它自己的价格。而Order Line又更进一步将计算工作转交给Product做;请注意,这时候会传一个参数给Product。同样滴,为了算出折扣,Order也会呼叫Customer的某个方法。因为Customer在做这件事时需要从Order得到一些信息,所以它又做了一个在进入Order的调用(getBaseValue),已取得必要资料。
两者的比较:我们可以很清楚地看出者两个互动情形采用不同的设计风格。第一个图采用是集中式控制设计风格,它让某个参与对象负责整个处理过程中的绝大部分工作,其他参与对象只负责提供资料而已。第二个图则采用分散式控制风格,它将处理过程分散到许多参与对象身上,每个参与对象都负责完成验算中的一小部分工作。
大部分一些面向对象编程的新手,比较常用采用集中式控制的设计风格。许多方面,这种做法是比较简单的,因为所有的处理过程都会在同一个地方完成;另一方面,如果我们采用分散式控制设计风格的话,追踪程序如何执行时,就会有一种在对象间绕来绕去的感觉(ps:别人家的代码都怎么写,我也是醉了)。
为了达到比较好的设计结果,我们主要的设计目标之一就是:尽量将变动可能发生的影响局限在一小块范围内。资料跟存取此资料的行为通常会跟着一起变动,所以把资料跟使用它的行为放在同一个地方就是面向对象设计的第一个原则。
评注:将一块工作分开来,让我们在有机会再不同变异点应付多变的需求。不过,它要付出的成本也不少。所以,我们对象过程式设计的概念有简单结构,慢慢转向预先产生复杂结构,以应付需求未来可能发生的变动。稍后,又在转变成:先采用简单结构,在随需求逐渐演化成必要的复杂结构。
1、参与对象的产生于删除动作
时序图中有一些额外的表示法可以秀出参与对象的产生于删除动作(详见下图)。为了产生参与对象,我们会把信息箭头直接指向参与对象的长方形,如果参与对象在被产生之后立刻要做些事情,那么你可以在参与对象长方形的下方马上启动它的活化长条。
在有垃圾回收机制的环境下,我们不会直接删除对象,不过大家还是可以用X标记指出:者时候已不再需要对象,请回收它。对一些结束操作加上X标记也是恰当的做法,因为它代表这个对象已经不再是可用的。
2、循环和条件方式逻辑等互动框
时序图中常见的一个议题是如何画出循环与条件行为。我们要说明的是:时序图并不擅长做这种事。如果你希望画出这种类型的控制结构,那么最好是用活动图,甚至是程序代码本身。
常用的互动框运算子:
运算子 |
意义 |
alt(多选一) |
在多个片段中一个执行它;成立条件为真的那个片段才 会被执行。
评注:alt相当于if...then..else..endif 或 switch case |
opt(可选用的) |
可以选择要不要执行的一个片段;它的成立条件为真时,这个片段才会被执行。它相当于只有一条时间实例序列的alt互动框。
评注:opt相当于if...endif |
par(平行的) |
可平行执行的几个片段
评注:不同片段间的执行顺序不是很重要。不过同一个片段内的信息还是要依时间轴先后执行。 |
loop(循环) |
这个片段可能会被执行多次,它的成立条件是反覆是否要执行的基础。
评注:一般在loop中,我们会用break运算子以中途跳出循环的正常执行过程。 |
critical(临界区域) |
在这个片段里面,每次只能有一个执行体(thread)执行它 |
neg(否定) |
代表这个片段中所秀出来的是无效的互动情形
评注:更net相对的运算是assert(假定),代表这个片段中所秀出来的唯一有效的互动情形。除此之外,我们可以用ignore(忽略)运算子说明互动框中会认为这些信息不重要,所以忽略他们(直接不画出来就好了);也可以用consider(重视)运算符说明互动框中认为只有这些信息是重要的,因此会秀出他们。 |
ref(参照) |
用来指向另一张图中所定义的互动情形。我们会让它的框涵盖互动情形中有关联系的生命线。
评注:ref的语法为
互动情况名称(参数串列):传回值
|
|
|
3、同步与非同步呼叫
对UML2版来说,这些小差异是很重要的。因为在UML2版中,填满的箭头代表同步消息(synchronous message),而空心、棒状的箭头代表非同步消息(asynchronous message)
4、何时使用时序图
想知道几个对象在某个使用案例中的行为时,请使用时序图。时序通非常有利于秀出对象间的合作情形,它不适合用来产生对象行为的精确定义。如果你想了解某个对象在不同使用案例间的行为时,可以用状态图。如果你想知道很跨多个使用案例或执行体的行为