53.规范表示法(canonical notation)
UML定义了规范表示法,它用单色线和文字表示任何模型。这就是UML模型的标准"出版格式",可用于印刷图。
图形编辑工具可以扩展规范表示法并且提供交互能力。比如,一个工具提供突出屏幕中被选择元素的能力。其他交互能力包括模型中的导航和按照选择的特性过滤显示的模型。这种格式是暂时的,并且不受UML制约。交互显示减少了模棱两可的弊端。所以,UML标准的焦点是印刷的规范形式,一个交互工具可以而且应该提供交互扩展。
54.基数(cardinality)
基数是集合中元素的数量。它是一个具体的数值。基数不同于多重性,多重性是一个集合拥有的可能基数的范围。
讨论
请注意基数这个术语被许多作者错用为我们所谓的多重性,但是基数在数学上定义为一个数字,而不是数的范围。这正是我们所用的定义。
55.修改事件(change event)
由于所引用的一个或多个值的改变而使布尔表达式得到满足的事件称为修改事件。
见监护条件(guard condition)。
语义
修改事件包含由一个布尔表达式指定的条件。事件没有参数。由于条件所依赖的一个或多个变量的改变而使条件(从假)变成真时,事件就发生。
这种事件隐含一个对条件的连续的测试。而实际上,通过分析条件输入改变的时间点,开发者可以在良好定义的、抽象的时间上执行测试,这样就不需要连续的测试了。
当表达式的值从假改变到真(一个正值状态变化)时,事件就发生。当这种情况发生时事件发生一次,除非值又先变成假,否则,事件不会再发生。
请注意与监护条件的区别。当转换上的事件发生时,监护条件只计算一次。如果条件是假的,那么转换不激发并且事件被遗失(除非这个事件又触发了其他的转换)。一个修改事件隐含连续计算,并且当值变成真时事件发生一次。在那个时刻,它可以触发一个转换或被忽略。如果它被忽略,因为条件还是真所以修改事件不触发处于后继状态的转换。修改事件已经发生,因此被抛弃。条件必须变成假,然后再变成真时,才能引发另一个修改事件。
布尔表达式的值必须是对象的属性,这个对象拥有包含从它可到达的转换或值的状态机。
表示法
与信号不同,修改事件没有名字并且不被声明。它们仅仅被用作转换的触发。修改事件表示成关键字when,后面是圆括弧中的布尔表达式。例如:
when(self.waitingCustomers>6)
讨论
修改事件是对条件满足的测试。实现起来是十分昂贵的,虽然有技巧来编译它而不必连续地测试,但是修改事件本身隐含着高成本和一种特定的关系,即值的改变与由它触发的结果之间的直接因果关系。有时,这是令人满意的,因为它封装了结果,但应该小心使用修改事件。
修改事件表示对对象可见的值的测试。如果一个对象中属性改变意味着触发另一个对象(该对象不知道第一个对象的属性改变)的修改,那么这种情况应该建模成属性拥有者上的修改事件,它触发一个内部转换,向第二个对象发送信号。
请注意一个修改对象并不是明确地向任何地方发送。如果试图与另一个对象进行明确地通信,应该用信号。
修改事件可以通过多种方式实现,有些通过在合适的时间点在应用程序内部进行测试,而有些则通过利用基本的操作系统机制来完成。
56.可变性(changeability)
说明某属性值或者链是否可变的一种特性。
语义
本特性可以赋给关联端或者属性。此特性还可以赋给一个类,表明该类中的所有关联和属性都满足本特性(例如,特性值为"冻结")表示该类的对象的值在初始化后不可改变)。在特性表中代表可变性的关键字可取下列枚举值。
可变的(changeable)
属性值可以任意变换,包括在多重性允许的范围内增加或者删除值。链可以随意变更,或者在多重性及其他约束允许的范围内被增加或者删除。如果没有指定其他值,则以此为默认值。
冻结的(frozen)
属性值在初始化后不可变更;不可增加或者删除变量。当关联的另一端(即与带有冻结值的一端相对另一边)的对象初始化后,则不可增加,删除或者修改链。但是,当另一端生成新的对象时,作为初始化的一部分,将生成指向冻结端的新连接。
只可增加(addonly)
如果多重性不是固定值,或者尚未达到最大值,则可以增加新的属性值。一旦生成,只要类的对象仍然存在,就不能更改或删除属性值。可以增加新的链,但是不能在其生成后进行修改或删除。当一个参与者对象被删除时,其中包含的所有链,无论是否是"只可增加"类型的,都将被删除。
57.子(child)
泛化关系中更具体的元素。被称为一个类的子类。一个或者多个子关系的链称为后代。反义词:父(parent)
语义
子元素继承了其父元素的特征(同样间接继承了其祖先的特征)而且可以声明自己附加的特征。它还继承了涉及其祖先的关联和约束。子元素遵循替代原则--即描述符的实例满足任何该描述符祖先声明的变量。子的实例是其父的间接实例。
58.类(class)
说明一系列拥有相同的属性,操作,方法,关系,行为的对象集。一个类就代表了被建模系统中的一个概念。根据模型种类的不同,此概念可能是现实世界的(对于分析模型),或还可能含有算法和计算机实现的概念(对于设计模型)。类元是类的泛化,包含其他类似类的元素,如数据类型、参与者、构件。
语义
对对象集的数据结构及行为的描述称为类。类用于声明变量。作为变量值的对象必须属于一个类,该类应与声明该变量的类兼容--也就是说,该类必须是声明变量的类或其后代类。类还用来实例化对象。一个创建操作生成给定类的一个新实例。
类的实例对象,是这个类的直接实例,同时是该类的父类的间接实例,对象保存
每一个属性的值;接受本类的所有操作和信号。同时还可以出现在与本类或本类的父类相关的关联链中。有些类可能不会被直接实例化,而只用于描述其后代类共享的结构;这种类被称为抽象类。可以实例化的类称为具体类。
一个类还可以被当作全局对象,该类任何类作用域属性是这个隐式对象的属性。这样的属性有全局性,在整个系统中拥有唯一的值。类作用域的操作是指适用于类本身,而不适用于对象的操作。最常见的类作用域操作就是创建操作。
在UML中,类是一种类元,类元包括一系列类似于类的元素,但是它的完整表述仍在类中。
结构
一个类有名字以及一些操作、方法和属性。类可能属于某种关联、泛化关系、依赖关系,或者约束关系。类是在其命名空间内声明,比如在一个包或者其他的类内,在其名字域内有不同的特性,比如多重性或者可见性。类还拥有其他不同特性,比如用于说明它是抽象类还是主动类。还将有一个状态机来说明类的反应行为--就是说当接收到某事件时的反应。类可声明其所处理的事件集(包括异常处理)。类可为由零个或多个的接口提供实现,或为一个行为实现提供不同类型。接口列出了该接口支持的并由类实现的操作集。
类包含属性表和操作表,它们各自在类内建立了一个命名空间。继承的属性和操作同样出现在相应的命名空间内。属性的命名空间中还包括伪属性,例如联系的角色名,用于类和用于涉及该类或它的一个祖先的泛化关系的区分符的,每个名字在该类和其祖先中仅能声明一次,否则,将产生冲突,模型建立错误。如果所代表的操作相同,相应的操作名可重复,否则将产生冲突。
类也是一个命名空间,并为嵌套类元声明建立了的作用域。嵌套类元并不是类的实例的结构性部分。在类对象与嵌套类对象之间并没有数据联系。嵌套类是一个可能被外层类的方法所使用的类的声明。在类内的类声明是私有的,除非清楚的设定为可见,否则在该类的外部是不可访问的。 没有可见的标识符来表示嵌套类的声明。只有在利用超级链接工具时,才有可能对它们进行访问。嵌套的名字必须用路径名来指定。
图13-40 基类声明
表示法
一个类是用实线边框的矩形来表示的,矩形用两条水平线分为三部分。顶部分格包含类的名字以及其他适用于整个类的特性。中部分格包含属性表。底部分格包含操作表。中部和底部可以在类的标识内隐藏。
使用 类是在类图中声明的,并且在许多其他图中被使用。UML为声明和使用类提供了一种图形表示法,同时为了在其他模型元素的指明类提供了文字表示法。在类图中对类的声明定义了类的内容:类的属性、操作以及其他特性。其他的图中定义与类相关的其他关系和附件。
图13-40表示了带有属性和操作的基类声明。
图13-41表示了相同类的声明,其中包括了更详细的内容,主要是与实现有关的性质, 例如可见性、类层次作用域的创建操作以及依赖于实现的操作。
所有与类相关的内部信息在图13-42中没有表示。这些信息仍出现内部模型中,通常会在至少一幅图中表示。
表示选项(presentation options)
隐藏分格。隐藏属性和操作其中之一或全部(图13-43)。没有表示的间隔将不出现分隔线。如果某个分格被隐藏,则无法推断其中的元素是否存在。请注意,空的分格(即有分隔线,但是没有内容)说明相应的列表中没有元素。如果使用某种筛选程序,则说明没有符合其要求的元素。例如,只有公共操作是可见的,则相应的操作分格为空就说明没有公有操作。对于私有操作不能得出任何结论。
图13-41 带有可见性特征的详细的类声明
图13-42 隐藏了所有详细内容的类的例子
图13-43 隐藏了属性以及非公共操作的类的声明。
附加分格 附加分格用于表示其他预定义或者用户定义的模型特性--例如用于表示事务规则,职责,变化,信号处理,异常处理等等。附加分格的顶部写有分格名字,并用特殊的字体与其内容区分开来(见图13-44)。标准分格(属性,操作)则不需要分格名字,尽管在只有一个分格可见时,可能会用分格名以示强调或区别。多数的分格中只是简单的字符串表,其中的每一个字符串代表一个特性。这里的"字符串"包括图标和嵌入的文档,例如电子表格或者图形。还可能有更复杂的格式,但是UML并没有为这些格式作特别说明。这种说明是用户或工具的职责。如果分格的性质可以从其中内容 的格式判断出来,则分格的名字可以省略。
见字体的使用(font usage)、字符串(string)
图13-44 带有额外命名的分格的类的声明
图13-45 带有构造类型的类
构造型 构造型在类的名字上方以括在书名号(《》)内的文本字符串来表示(见图13-45)。还可以在分隔的右上角用图标来代替这行文字。带有构造型图标的类的标识可以被"压缩"为只表示类的构造类型图标,并在图标内,或者图标下方标明类的名字(见图13-170)。类的其他内容被隐藏。
见 构造型(stereotype)。
格式指导
。 用正常字体在类名字的上方书写构造类型
。 用黑体字居中或者向左对齐,书写类名
。 用正常字体,向左对齐书写属性和操作
。 用斜体字书写抽象类的名字,或者抽象操作的符号
。 在需要处表示类的属性格和操作格(在一套图中至少出现一次),在其他上下文或提及处可以隐藏它们。最好为一套图中的每个类定义一个"原始位置(home)",并在此处对类进行详细描述。在其他位置,可以使用类的最小化表示形式。
讨论
类的概念既适用于逻辑建模中的一些用例,也适用于实现。既包括类型的概念,又包括类的实现的概念。在UML中,在特定语义模糊的地方,一个对象可以有多个类,还可以在运行时变更类。在多数程序设计语言中常见的有更严格限制的类的定义,可以被视为特殊的类。
标准元素
实现类(implemetationClass),类型(type)
59.类图(class diagram)
类图,用静态视的方式表示声明的(静态的)模型元素,比如类、类型及其元素
及相互关系。类图可能表示包,甚至包含嵌套的包的符号。类图包含一些具体的行为元素,如操作,但是它们的动态特征是在其他图中表示的,例如状态图、合作图。
见类元(classifier)、对象图(object diagrams)
表示法
类图是用图形方式表示静态视图。通常,为了表示一个完整的静态视图,需要几个类图。每个独立的类图不需要说明基础模型中的划分,即使是某些逻辑划分,例如包是构成该图的自然边界。
60.状态类(class-in-state)
一个类,以及其对象可能存在的状态。
见活动图(activity graph)
语义
带有状态机的类有许多状态,每个状态说明了处于这种状态的实例的行为,取值,以及约束。在某些情况下,某些属性,关联,或者操作仅适用于处于特定的一种或几种状态的实例。还有些情况中,某个操作的参量必须是特定状态下的对象。通常情况下,上述特殊情况只是行为模型的一个部分。但是有时,最好在交互视图或者静态视图中将它们直接标明。
状态类仍然是类,只是带有类的对象可能拥有的合法状态。如果该类有并发的子状态,那么状态说明将列出一系列该类的对象可能同时存在的几种状态。状态类可以被当作一种类元。它的行为类似所描述类的一个子类。它可以被当作变量类或参数类使用。它可以参加那些只适用于所给定状态的对象的关联。在图13-46中,请看SubmittedPaper与ConferenceSession之间的Assignment关联。这种关联仅在SubmittedPaper处于accepted状态时才适用(目标多重性为1),而在rejected状态时则不存在。对于SubmittedPaper而言,目标多重性为0或1,因为类中同时含有accepted和rejected的报告。然而在对处于accepted状态下的SubmittedPaper和ConferenceSession间的关联建模时,那么其目标多重性就是1。
图13-46 状态类
在表示活动图中操作的输入输出值时,状态类元素也是有用的。
表示法
状态类用类符号表示,在类名后,跟着在方括号内的状态名(类名[状态名])。方括号中也可以是用逗号分开的几个并发状态名,表示对象可以是这些状态中的几种。
讨论
状态类和动态类元是为了实现允许对象在其生命周期内改变结构而采用的两种不同方法。根据实现环境的不同,其中之一可能更方便适用。
61.类名(class name)
每个类都必须有一个非空的类名,这在类的外壳(例如包或者包含类)内对于类元而言是唯一的。名字的作用域是类的外壳包,或者可以看到该包的其他包。
见名字,以得到关于命名以及唯一性规则的完整说明。
表示法
类名在表示类的矩形内顶部里表示。名字分格中可能还有关键字或者构造型名,和/或构造类型图标,以及用花括号括住的一系列标签值。(见图13-47)。
处于书名号内可选的构造类型关键字可置于类名字的上方,和/或在分格的右上角表示构造类型的图标。构造型名不得与预定义的关键字重复,例如enumeration。
图13-47 名字分格
图13-48 在其他包中的类的路径名
接下来表示类的名字,类名用粗体字在分格的水平正中标出。如果此类为抽象类,类名就用斜体表示。注意,任何明确的泛化状态的说明(例如抽象或者具体),其字体应大于类名所用的字。
在类名下,可以在方括号中列出标识特性的字符串表(元模型的属性或者特性值)。此列表可以表示那些在UML中没有符号表示的类级属性,还可以表示特征值。可以用某些无值的关键字来表示某种属性与值的联合体。例如,一个leaf类表示属性为{leaf},与{isLeaf=true} 作用相同。
没有特别说明,假定表示在某个包内的类是时在此包内定义的。要引用在其他包中定义的类,作为名字分格中的名字字符串使用的语法为(图13-48)
Package-name::Class-name
用符号(::)将包的名字与类名分开,从而提供完整的路径名。如果路径名可以区分它们,在不同包内的不同的类中可以使用相同的类名。但是这种情况容易造成错误,应该尽量避免。
在文本的描述中,也会引用类,尤其是在属性和变量的类型说明中。在文本
描述中,引用一个类只要使用它的名字,包括可能包的名字,这服从于表达式的语法规则。
62.类元(classifier)
类元是一种描述行为和结构特征的模型元素。类元的种类包括类、行为、构件、数据类型、接口、节点、信号、子系统以及用例。类是最常见的类元。虽然每种类元都有各自的元模型为代表,但是它们都可以按照类的概念来理解,只是在内容和使用上有某些特殊限制。类的大多数特性都适用于类元,通常只是为每种类元而增加了某些特殊限制条件。
见泛化元素(generalizable element)、静态视图(static view)。
标准元素
枚举(enmeration)、位置(location)、元类(metaclass)、持久性(persistence)、强类型(powertype)、进程(process)、语义(semantics)、构造型(stereotype)、线程(thread)、效用(utility)。
63.类元角色(classifier role)
这是在合作中的一个片段,用于描述各个参与者在合作中的角色。
见合作(collaboration)。
语义
合作说明了在一系列参与者之间的交互方式,它是类或者数据类型的实例。类元角色是对一个参与者的描述。每个角色处在自己独特的上下文中,是类元的一个独特使用。一个类元可能有多个角色,每个角色在一个合作中与其他角色有着不同的关系。一个角色不是独立的对象,而是某个可能参与合作实例的所有对象的描述。每当这个合作实例化时,就有不同的对象与链来扮演这些角色。
每个类元角色有其类元(其基础)的引用以及多重性。基础类元限制了可以充当角色的对象种类。对象所属的类可以与该基础类元的类相同,或是其子类。多重性说明在合作的一个实例中,同一时刻有多少个对象来充当这种角色。
类元角色可以有名字,也可以匿名。当需要多重类元时,类元角色可以有多个基础类元。
类元角色可以通过关联角色与其他类元角色相连接。
对象。一个合作代表了为了完成某个目标而共同工作的一组对象。角色表示一个对象(或一组对象)在完成目标的过程中所应起的那部分作用。一个对象是角色所属的基类直接或者间接的实例。在合作中,不需要基类所有的对象都出现,同一个基类的对象在一个合作中也可能要充当多个角色。
在不同的合作中,同一个对象可以充当不同角色。合作代表了对象的一个方面。单独的一个物理对象可能包括多个方面,因此隐含了该对象与其起作用的合作间的联系。
图13-49 类元角色
表示法
类元角色是用类元的符号(矩形)表示,符号中带有用冒号分格开的角色名和类元名字,即:角色名:基类。一个角色不是独立的对象,而是一个用于描述不同类元实例中出现的多个对象的类元。
角色名和类元的名字都可以省略,但是分号必须保留,从而与普通的类相区别。在一个合作中,由于所有的参与者都是角色,因而不易混淆。
类元角色可能会表示类元特征的一个子集,即在给定的上下文中的属性和操作。其余未被用到的特征将可以被隐藏。
图13-49表示了类元角色不同的表示法。
标准元素
被销毁的(destroed)、新建(create)、暂时(transient)。
64.客户(client)
是指一个需要其他元素提供服务的元素。它用于描述在依赖关系中的角色。在表示时,
客户出现在依赖关系箭头的尾部。反义词:提供者。
见依赖(dependency)
65.合作(collaboration)
是对对象和链总体安排的一个描述,这些对象和链在上下文中通过互操作完成一个行为,例如一个用例或者操作。合作由静态部分和动态部分组成。静态部分描述在合作实例中对象和链可能承担的角色。动态部分包括一个或多个动态交互,表示在执行计算过程中不同时间里合作中消息流。
见关联角色(association role)、类元角色(classifier role)、交互(interaction)、消息(message)。
语义
一组对象在给定的上下文范围内,为了完成某个目标而交换消息,从而实现了一种行为。要理解设计机制,应该重点着眼于实现一个或一组相关目标时涉及的对象和消息,
它们在一个更大的系统中也完成其他目标。使对象和链共同工作以实现某种目标而进行的安排称为合作;在合作中实现行为的消息序列称为交互。合作是对"对象的组织"的一种描述。它是大型复杂模型中的一个片段,在模型中合作是为了实现一个目标。
例如,商业销售代表了对于一些对象的一种安排,这些对象在事务处理中有着特定的相互关系。在事务处理范围之外,这种相互关系就没有意义了。销售的参与者包括销售者,购买者和代理人。为了实现某个特殊的交互,例如出售房屋,参与者们交换特定消息序列,例如报价或者签订合同。
在合作中流动的消息流,可以选用状态机来进行描述,状态机将规定合法的行为顺序。状态机中的事件代表了合作中各角色间的消息交换。
合作由角色组成。角色是指在合作中起作用的类元或者关联的一部分。实例化时,角色可以带有类元或者关联的实例。同一个类元或关联可以承担不同的角色;每个角色有不同的对象和链。例如,在商业事务处理中,两个公司中一方可能是销售者,另一方为购买者。seller和buyer是合作关系Sale中Company类所承当的角色。角色只在合作中才有意义;在合作之外就无意义了。实际上,在其他合作关系中,角色可能会对调。某个对象在一个合作实例中可能是seller,而在另一个中就是buyer。同一个对象可能在不同的合作中承担多种角色。这与角色在关联限定的作用域不同。无论对象是否参与了关联,关联所描述的关系对于一个类在所有的上下文中有全局性的意义。合作所描述的关系局限于特定的上下文中,在此范围之外,关系就没有意义了。
实现。合作实现了某个操作或者用例。它描述了该操作或者用例的执行实现所处的上下文--- 即,开始执行时,对于存在的对象和链的安排,以及在执行过程中生成的和销毁的实例。交互中的行为序列的说明可以用顺序图或者合作图表示。
合作也可完成类的实现。类的合作是类操作的合作的联合。为同一个类,系统,子系统可以设计不同的合作;每个合作表示了与实体的一个视图相关的属性,操作和相关对象的一个子集,(例如某个特定操作的实现)
模式。参数化的合作代表了一种可以在不同的设计中重用的设计结构。通常基本类元是参数。这种参数化的合作符合模式结构。
见模板(template)
通过给基础类元参数提供具体的类元,可以得到设计模式的实例。每个实例产生模型中特定的类元集上的一个合作。一个模式可与模型中的不同类元集多次绑定,从而避免为每次出现定义新的合作。例如,一个模型视图模式定义了模型元素间的常规关系,它可与代表模型视图对的多个模型视图类对进行绑定。每个实际模型视图类代表了对模式一次绑定。这些类对中的某对可能是房屋和房屋里的图像,另一对可能是股票和表示股票当前价格的图形。
请注意,一个模式还包括使用指导,以及对与其优缺点的明确描述。这些内容可
以作为注释,或写在单独的文本文件中。
合作的层次 合作可以在不同的粒度水平(granularity)上表示。一个粗粒度的合作可以通过进一步细化成为另一个粒度更细的合作。这是通过扩展一个或者多个操作来实现由高层次的合作到低层次的合作的细化。
合作还可以通过下级合作来实现。每个下级合作实现整个功能中的一个部分,并有其独立的角色集。整体合作中的每个角色可以与嵌套部分中的一个或者多个角色绑定。如果一个外层角色被绑定为多个内层角色,则其隐含了与低层合作的连接。这是用例之间交互作用的唯一方式。一个设计思路是从内层向外的。首先构造内层,限定角色,而后将它们结合生成外层,拓宽角色使其拥有多重责任。
运行时绑定 。在运行时,对象和链与合作中的角色绑定。通常在不同的合作中,一个对象可以绑定到一个或 者多个角色上。如果一个对象绑定到多重角色,那么它代表了角色之间的一种"偶然"交互关系--即,这种交互不是由角色本身固有的,而只是它们在更广的上下文中使用时的一种副作用。通常一个对象在组成大型合作的多个协
作中承担角色。这种合作的重叠提供了一种隐含的,合作间的控制和信息流动。
结构
角色。一个合作包括一系列的角色,每个角色是一个或者多个基础类元(类元角色)
或者基础关联(关联角色)的引用。角色是合作中一个位置,它描述了类元或关联在作中的一次使用。角色本身也是一个类元,合作实例的角色绑定于对象是角色的暂时实例。在一个合作的实例中, 每个类元角色绑定于一个对象,每个关联角色绑定于一个连接。在同一个合作的实例中,一个对象可以绑定到多个类元角色上,但是这是不常见的,而且可以通过适当的约束加以避免。在同一个合作的实例中,同一个类的对象可以充当不同的角色。每个对象有着与其角色相适应的关系。每个类元角色拥有在合作中用到的类元的属性的一个子集。其余的属性与本合作无关,但是可能在其他合作中有用。如果有同一个基础类元含有多重角色,每个角色应有各自的名字以示区别。如果在合作中某个基础类元只用到一次,角色可以没有名字,因为类元不会混淆。角色定义了合作的结构。
如果支持多重类元,角色就可以有多个基础类元。 绑定于类元角色的对象是每一个基础类元的一个实例。
泛化。一个合作还可以包含泛化关系和约束。这是对于参与合作的类元在其各自外
层合作中可能拥有的关系的补充。只用当合作中的类元是参数形式时,泛化才是必要
的。否则,它们的泛化结构只是作为其类元定义的一个部分出现,在合作中不可更改。
在参数化的合作中(模式中)某些类元角色可能是参数。两个参数化类元之间的泛化关系说明任何绑定到角色的类元必须满足泛化关系。(绑定到父角色的类元必须是绑定到子角色类元的祖先。它们之间不需要是父子关系。)
例如,[Gamma-95]Composite模式表示了一个对象递归树,其中Component是树的一般元素,Composite是递归元素,Leaf是叶元素(图13-50)。Component是Leaf和Composite的父节点。Component是元素的聚集(循环)。Component,Composite,Leaf是模型中的参数。当模型被使用时,它们由具体的类替换。任何绑定到模式的具体类必须遵循Component与其子Composite,Leaf之间的祖先-后代关系。具体的
例子可以是Graphic,Picture,Rectangle;DirectoryEntry,Directory,File;或者任何递归类。如果一个绑定不满足泛化约束,则其形式错误。
图13-50 模式:一个参数化的合作
约束。参数化的和非参数化的角色都可以有其约束。这些约束是对于绑定到角色的类元约束的补充。约束适用于任何合作实例。
消息。合作可以由一系列消息来描述其动态行为。带有消息的合作称为交互。可以有多重交互,每个交互说明同一个合作中的一个部分。例如,一个交互可以描述一个操作的实现,另一个交互说明另一个操作。消息带有其中的顺序信息;顺序的信息序列的作用与定义由消息触发的状态机作用相同。
资源。合作可能代表一个类,用例,或者方法(合作是一个操作的实现,而不是操作的说明)。合作描述了源元素的行为。
表示法
合作图由类的符号(矩形)代表类元角色,关联路径(实线)代表关联角色,关联角色路径上带有消息符号。不带有消息的合作图标明了交互作用发生的上下文,而不表示交互。它可以用来表示单一操作的上下文,甚至可以表示一个或一组类中所有操作的上下文。如果关联线上标有消息,图形就可以表示一个交互。典型的,一个交互用来代表一个操作或者用例的实现。合作图表示了合作中作为类元角色的对象所处的位置。类元角色与类元的区别在于它既有名字又有类名,语法为:角色名:类名。角色名和类名都可以省略,但是冒号必须保留。图中还表示了作为关联角色的对象之间的链,包括代表过程参数、局部变量以及自连接(self-link)的暂时性链。多重关联角色如果与不同的类元角色连接,则可以有相同的关联名字。连接线上的箭头表明了箭头方向上的导航性。(在对象之间的线上的箭头表明该连接只有单方向导航性。邻接于线的箭头表明消息在连接线上按照所给方向流动。在单向连接中,消息不能返回,所以消息流动必须与导航箭头相吻合。)
类元角色中的单个属性值通常不明确标出。如果消息必须传递给属性值,则应该将属性建模为使用关联的对象。
工具中还可以使用其他图形来作为对关键字作补充,或者取代它。例如,每种生命期可以用不同的颜色表示。还可以用动画来表现元素的生成和销毁,以及不同时间内系统的状态。
操作的实现(Implementation of an operation)
表示操作实现的合作包括了目标对象角色的符号,以及目标对象实现操作时直接或者间接用到的其他对象的符号。关联角色上的消息表示了一个交互中的控制流。每个消息表明了操作的方法中的一个步骤。
一个合作描述操作,还包括了代表操作参数和执行中生成的局部变量的角色符号。在执行中生成的对象可以用{new}指明;在执行中销毁的对象用{destored}指明;在执行
中生成,随后又被销毁的对象用{transient}指明。没有带关键字的对象是在操作开始前就存在,并且在操作结束后还存在的对象。
实现一种方法所用的内部消息被编号,从1号开始。对于一个过程化的控制流,顺序的消息号码用"点(dot)"方法,按照嵌套层次排序。例如,第二层的步骤是消息2;它下一级的步骤就是2.1。对于并发的对象间异步消息交换,所有的消息编号都是在同一层的。(就是说它们没有嵌套关系)。
见消息(message),以全面了解包括顺序在内的消息语法。
完整的合作图表示了操作中所有对象和链的角色。如果某个对象没有表示,则假
定它未被使用。但是假定合作图中的所有对象都被该操作使用是不安全的。
举例
在图13-51中,redisplay对象调用Controller操作。当操作被调用时,它已经与将要显
示图像的Window对象有了连接。它还与Wire对象有连接,该对象中的图形将被显
示在窗口中。
redisplay操作的定层实现只有一个步骤--调用Wire对象中的diaplayPositions操作。这个操作的序号为1,因为它是最上层方法中的第一步。这个消息流被传递给后面将用到的Window对象的引用。
displayPositions操作调用同一个Wire对象中的drawSegment操作,这个调用的
编号为1.1,它通过self链传递。星形符号表示操作的重复调用;详细内容在方括号中
说明。
每个drawSegment操作访问两个Bead对象,编号分别为i-1和i 。虽然在此上下文中,Wire与Bead之间只有一个关联,但是需要两条指向两个Wire对象的链。对象被标以left和right(这些是合作中的类元角色)。每个链上有一条消息传递。消息编号为1.1.1a ,1.1.1b。这表明它们是操作1.1的步骤;最后的字母表示两条消息可以并行传递。在通常实现中,它们可能并不会并行执行,但是由于它们被声明为并行的,所以可以按照任何顺序执行。
图13-51 带有消息流的合作图
当得到两个返回值r0,r1后,可以执行操作1.1之下的另一个步骤了。消息1.2是一个传递给Line对象的创建消息。实际上,消息到达Line类(至少在原理上是这样),该消息创建了一个与发送者相连接的新的Line对象。新的对象带有标签{new},表明它是在操作的执行过程中被创建的,并且将在操作结束后继续存在。新建的链带有标签《local》,表明它不是一个关联,而是过程中的局部变量。局部变量是暂时性的,当过程执行完毕后将消失。因此不需要为它加上标签{transient}。
步骤1.3通过新创建的链向新创建的Wire对象发送一条display消息。指向Window对象的指针作为参数一同传递,从而使得它可以被Line对象作为《parameter》链访问。注意,与Line对象相连的window对象就是与原始的Controller对象相关联的Window对象;这一点对于操作而言很重要,并且在图中已标出。在最后一步1.3.1中,要求Window对象创建与Line对象的连接。这个连接是一个关联,所以它有角色名contents,并且标以{new}。
想象擦去所有临时连接,就得到系统的最终状态。在Contriller和Wire之间,以及Wire和它的部分Bead之间,Controller与Window之间,Window与其Contents之间都有连接。但是,一旦操作完成,Wire就不能访问包含它的Window了。在这个方向上的连接是暂时的,当操作完成后就消失了。同样的,Wire对象也不能再访问用于对表示它的Line对象了。
实例级合作(instance-level collaboration)
与许多模型元素类似,合作可以用描述符或者实例表示。一个描述符级合作表示了对象之间可能的关系;合作可以被多次实例化从而生成合作实例。每个合作实例表示了特定对象之间的关系。
应该用哪种形式来为某个情景建模呢?如果图表示的是可能事件,则必须使用描
述符级合作图。对象图没有偶然性。它们没有条件判断或者循环;这些是普遍性描述中的内容。一个实例没有取值范围,或者许多可能的控制路径;它只有一个值和唯一的历史。
如果图表示的是属性或者参数具体的值,如果图表示的是可变大小多重性的对象或者链的具体数目,或如果图表示的是对于执行中的分支或者循环的特定选择,这样的图必须是实例级合作图。
在许多情况下,两个视图都可以使用。当计算中没有分支时就是这样。如果任何执行都是其原型,描述符形式和实例形式间没有太多区别。
66.合作图(collaboration diagram)
表示角色间交互的视图--即,合作中实例及其链的位置。与顺序图不同,合作图明确的表示了角色之间的关系。另一方面,合作图也不将时间作为单独的维来表示,所以必须使用顺序号来判断消息的顺序以及并行线程。顺序图和合作图表达的是类似的信息,但使用不同的方法表示。
见合作(collaboration)、模式(pattern)、顺序图(sequence diagram)
67.合作角色(collaboration role)
合作中的对象或者链的位置。它说明了在合作实例中可能出现的对象和链的种类。
见关联角色(association role),类元角色(classifier role),合作(collaboration)。
语义
合作角色是描述对象或者链。但是它不代表单独的对象或连接,而是当合作实例化
时,可以由对象或连接替换的位置。合作可以是类元角色,也可以是关联角色。一个
类元角色有一个或多个基础类元,可以实例化为对象。关联角色可有一个基础关联,且可实例化为链,该链可以是关联的实例,或是它的后代的实例。在许多情况下,关联只在合作中定义--即,说它只出现在承担角色的对象上,离开合作后就没有意义了。在这种情况下,可以省略基础关联。关联在合作中被隐含的定义了,在别的地方不可用。
表示法
合作角色可以是类元角色或者关联角色。
类元角色 类元角色是一个类元,用类的矩形符号表示。通常只表示名字分格。名字分
格包含字符串
classifierRoleName :BaseClassifierNamelist,
如果必要,基础类元名字可以包括外层包的详细路径名。(在不会产生混淆的情况下,工具可以允许简化的路径名)。包的名先于类名,并用双冒号隔开。例如:
display_window:WindiwingSystem::GraphicWindows::Window
类元角色的构造类型可以括在花括号的文本形式表示在名字字符串的上方,也可以用图标的形式标在右上角。一个类元角色的构造类型必须与其基础类元的构造类型
匹配。
代表一系列对象的类元角色,包含了在类的矩形框右上角的多重性指示符(例如"*")。它说明在合作的一个实例中,可绑定到该角色的对象数目。如果该指示符被省略,该值取1。
类元角色的名字可以省略。在这种情况下,类名之中要保留分号。代表类的匿名对象。
如果支持多重类元,角色可能有多个类元。对象就是它们之中每一个的实例。
类元角色的类可以被隐藏(包括分号在内)。
对于交互中创建的对象或者链,在它们的角色中有关键字new作为约束。对于在交互中销毁的对象或者链,其角色有关键字destroyed作为约束。即使对于某个没有名字的元素,也可以使用关键字。两个关键字可以同时使用,但是关键字transient可以用于代替new destroied。
关联角色。关联角色是一个关联,作为两个类元角色标号间的路径表示。路径可以赋以以下形式的名字:
associationRoleName : BaseAssociationName
如果基础关联的名字被省略,则没有基础关联。基础关联的角色名及其他修饰成 分将在路径上表示。
如果关联角色路径一端连接一个多重性大于1的类角色,则在关联的末端加上表示多重性指示符,以示强调。
图13-51 是一个关联角色的例子。
68.组合(combination)
将类元的两部分描述联系起来,从而组成为某元素的完整描述符,这两部分之间的相互关系称为组合。
见 扩展(extend)、包含(include)。
语义
面向对象的强大功能之一就是可以以渐增的方式模型将元素的描述组合起来。继承机制将与泛化相关的类元组合起来,生成对于类的有效完整描述。
组合描述符的其他两种方法是使用扩展和包含关系。(泛化本来也可以列在该条目下,但是由于其特殊的重要性,所以将它作为独立的基本关系)。
表示法
组合用带有构造类型关键字的虚线箭头表示。
详见扩展(extend)、包含(include)。
讨论
其他种类的组合也是可能的。某些程序设计语言,例如CLOS,实现了若干种不同的功能强大的方法组成。
69.注释(comment)
附在单个元素或者元素集上的注释。注释没有直接的语义,但是它可以表示对于建模者或工具有意义的语义信息或者其他信息,例如约束或方法体。
见注释(note)
语义
注释包含文本字符串,如果工具允许,也可以带有嵌入的文件。注释可以附在模型元素,表示元素,或者元素集上。它提供了关于任意信息的文字说明,但是没有语义作用。注释是提供给建立模型者的,可以用来搜索模型。
表示法
注释用注释的符号表示,即右上角向下折叠的矩形("狗耳朵"),并且用虚线与被注释的一个或几个元素相连(图13-52),工具可以使用其他的形式来表示或者导航注释信息,例如弹出式备注,特殊字体等等。
标准元素
需求(requirement),职责(responsibility)。N
图13-52 注释
70.通信关联(communication association)
描述相互连接的元素实例之间通讯关系的关联。在配置视图中,它是包含通讯关系的
节点之间的关联。在用例模型中,它是用例与通讯关联的参与者之间的关联。
见参与者(actor)、用例(use case)
71.分栏(compartment)
封闭形状符号的图形化分,例如将表示类的矩形水平的分成小矩形。每个分格表示它所代表的那部分属性。有三种分格:固定式,列表式,区域式。
见类(class)、类元(classifier)。
表示法
固定式分格用固定的图形形式以及文字部分表示固定的属性。图形形式由元素种类决定。例如类名分格,其中包含构造类型符号和/或名字、类名、表示类的不同的特性的字符串。根据元素的不同,其中某些内容可能被隐藏。
列表式分格包含了元素成分的编码表。一个例子是属性表。编码由成分的类型决定。元素表的顺序可以是它们在模型中的实际顺序,或者按照一个或多个特性排序(在这种情况下,自然顺序将不可见)。例如,属性的列表可以先按照可见性,再按照名字排序。根据模型元素特性的不同,表中的条目可以被表示或者被隐藏。例如,属性分格可能只表示公共属性。可以将构造型和关键字编入列表条目,从而将它们运用于每个独立成分。构造型和关键字可单独列入条目,从而适用于所有顺序成分。它们可以影响所有顺序成分,直至表结束或另一个这样的声明起作用。将《constructor》在操作表中单独列为一行,可以将随后的操作构造为构造符,随后的字符《query》将撤销前一个声明,并用《query》构造型代替它。
区域是一块包含表示元素子结构的子图的区域,通常暗含有递归。例如嵌套的状态图。子图的性质是模型元素特有的。在一个符号内同时包含区域和文字分格是合法的,但是将显得混乱。区域通常用于循环的元素,而文字用于没有循环子结构的叶元素。
一个类有三个预先定义的分格:名字,一个固定式分格;属性,一个列表式分格;操作,也是列表式分格。模型设计者可以在矩形中增加其他的分格,并在分格的上方用不同的字体(例如黑体)标出其名字。
图形语法依赖于元素和分格的种类。图13-53表示了信号分格。图13-54表示职责分格。
图13-53 类中命名的分格
72.编译时间(compile time)
是指在软件模型编译时出现情况。
见建模时间(modeling time)、运行时间(run time)
73.完成转换(completion transition)
是指没有专门的触发事件,而是由源状态中的活动完成来触发的转换。
见活动(activity)、转换(transition)、触发(trigger)
语义
完成转换是指没有明确触发事件的转换。转换的源状态完成了任何活动(包括嵌套状态)后,转换隐式触发。在组成状态中,活动的完成是由到达终态表示的。如果一个状态没有内部活动或者嵌套状态,则一旦入口和出口活动完成,完成转换将被立即触发。
如果一个状态有嵌套状态或活动,但是没有带有触发事件的输出转换,则完成转换不一定能发生。当封装的状态还在等待完成其活动时,某个事件可能触发了内部状态的转换,这样,可能会跳过完成转换。
完成转换含有监护条件和一个活动。通常无需出现只含有一个监护的完成转换,因为如果监护条件不满足,完成转换将永远不能发生(因为隐含的触发条件只出现一次)。有时如果触发转换将对象带出死状态,描述某些错误也可能有用。更多情况下,一系列带有监控的完成转换带有涵盖所有可能的条件,在这些条件下,当状态结束后,其中的某一个完成转换将立即触发。
图13-54 完成转换
完成转换还用于将初始状态和历史状态与其后继状态相连接,因为这些伪状态在完成其活动后将不会保持活动性。
举例
图13-54表示的是订票程序的状态机模型片断。在客户选择日期时,Selecting状态将一直处于活动状态,当客户按下"Done"键后,Selecting状态达到其最终状态。这触发了完成转换,转入Selected状态。
74.复杂转换(complex transition)
是指有多于一个源状态和/或多于一个目标状态的转换。它代表对引起并发数目变化的事件的响应。根据源状态和目标状态的数目,它可以是控制的同步,控制的分叉,或者两者兼而有之。
见分支(branch)、复合状态(composite state)、分叉(fork)、结合(join)、合并(merge)。
语义
从上层看,系统经历了一系列状态。但是从整体的观点来看,对于有分布性和并行性的大型系统而言,认为系统处于一种状态不足以说明问题。系统在同一时刻可以有多种状态。这些活动状态集称为活动状态结构。如果某个嵌套状态为活动的,所有包含此状态的状态均为活动的。如果对象允许并发,则可能有多个并发子状态为活动的。
在许多情况下,系统的活动可以被建模为一些相互独立或者交互不多的控制线程。每个转换只能影响活动状态结构中的一小部分状态。当转换发生时,未受影响的活动状态继续保持为活动的。某一时刻的线程可以被视为活动状态结构中的一个子状态,每个线程对应一个子状态。每个子状态对事件有独立的反应。如果活动事件的数目是固定的,则该状态模型就是一些相互交互的固定状态机的集合。但是,通常状态的数目(以及控制线程的数目)是随时间而变化的。一个状态可以转换到两个或者多个并发状态(控制分支),两个或者更多的并发状态可以转换到一个状态(控制的结合)。并发状态的数目以及变化由系统的状态机控制。
复杂转换是指源于或者指向一系列并发子状态的转换。复杂转换有多于一个的源状态和/或目标状态。如果该转换有多个源状态,则它代表了控制的结合,如果一个转换有多个目标状态,则它代表了控制的分叉。如果它同时有多个源状态和目标状态,则它代表了并行线程的同步。
如果一个复杂转换有多个源状态。只有当这些源状态都是活动的,转换才有可能被触发。而这些状态被激活的顺序是无关紧要的。如果所有源状态都是活动的,而事件发生,转换将被触发,如果监护条件得到满足,转换将激发。即使有多个源状态,每个转换也只被一个事件激发。UML不支持多事件;每个事件触发一个独立的转换,随后由控制结合点将它们与结果状态相连。
如果一个有多个源状态的复杂转换没有触发事件(即为完成转换),则当所有源状态被激活后,转换将被触发,如果监护条件满足,转换将激发。
当复杂转换激发时,所有源状态以及同一个组成中的状态都变为不活动的;所有目标状态及同一个组成中的状态变为活动的。
在更复杂的情况下,可以进一步扩展监护条件,使得在某些子状态活动时才允许转换激发。
举例
图13-55是一个典型的带有复杂的转换出入口的并发组成状态。图13-56是该状态机的一个典型执行过程记录。(活动的状态用蓝色表示)。记录表示了不同时刻活动状态数目的不同。
图13-55 分支与结合。
并发状态
除非对状态机非常仔细的设计其结构,否则一系列复杂转换可能导致不一致性,包括死锁、状态多重占有等问题。Petri网的理论充分的研究了这些问题,常见的解决办法是在状态机中引入良好结构规则来避免不一致性。这就是状态机的"结构化编程"规则。有许多不同的实现办法,各有利弊。UML接受的规则要求用与/或树将状态机精化。其优点在于:一个定义良好的嵌套结构容易建立、维护、理解;缺点是使一些有意义的结构被隐藏了。总而言之,这类似于为得到结构化的程序而放弃使用goto语句的做法。
一个复杂状态可被分解为几个相互排斥的子状态("或"分解),或者被分解为几个并发子状态("与"分解)。结构是递归的。通常"与"层可以替换"或"层。"与"层表示并发分解--所有子状态并发活动;"或"层表示顺序分解--每次只有一个子状态是活动的。可以从根节点开始递归扩展树的节点得到合法的并发状态序列。用所有的子状态代替"与"状态;用某个子状态代替"或"状态。这状态图的嵌套结构相对应。
图13-56 并发状态机的活动状态记录
举例
图13-57表示了图13-55的状态机对应的状态与/或树。一组典型的并发活动状态被标为蓝色。这与图13-56 的步骤3对应。
图13-57 嵌套状态的与/或树
如果一个转换进入到一个并发区域(region),则它进入到所有子状态。如果转换进到顺序区域,则它只进入了一个子状态。顺序区域中的活动状态可以变化,而在并发区域中,只要区域是活动的,则所有子状态都是活动的。但是,通常每个并发子状态又被进一步分解为顺序区域。
因此简单转换(有一个源状态和一个目标状态)必须连结两个在同一顺序区域内的状态,或者两个仅被"或"层分隔的状态。复杂转换将一个并发区域中的所有子状态与并发区域外的一个状态相连。(我们忽略了更复杂的情况,但它们也必须遵循上述原则)。换言之,进入并发区域的转换进入所有子状态,而离开并发区域的转换也离开所有子状态。
可以使用一种简化表示法:如果复杂转换进入并发区域但省略了一个或几个子区域,则隐含表示有转向子区域的初始状态的转换。如果某个子区域没有初始状态,则模型结构有错误。如果有复杂转换离开并发区域,则隐含了从各个被省略的子区域发出的转换。一旦转换发生,则子区域中的所有活动将终止--即强行退出。转换可连接到封装的并发区域本身,它隐含表示指向各个并发子区域的初始状态的转换--一个普通的建模情况。同样的,源自封装并发区域的转换表示强行退出各子区域(如果有触发事件)或者等待子区域结束工作(如果无触发事件)。
复杂转换的规则保证了无意义的状态组合不会并发处于活动状态。一系列并发子状态是封装的组成状态的一部分,它们要么同时活动,要么都不活动。
条件线程
在活动图中,带有分叉的线段可带有监护条件,被称为条件线程。当转换激发时,只有当监护条件满足后,有监控符号的线程才会被初始化。没有监护条件的线程则在转换激发后立即初始化。活动图中的并发必须是合理嵌套的。--每个分支点之后都要有相应的结合点。如果一个条件线程因为条件不满足而失败,在这条分支线上的活动被视为已经完成--也就是说,转换不再等待这个条件线程的控制流。如果分叉中的所有线程激发失败,则控制将立即从匹配的结合点重新开始。
条件线程等同于一个带分支和合并的图,该图处于活动图中的条件部分。
举例
图13-58 是带有两个条件线程的活动图。它表示一个航班的登机过程。在开始时,乘客必须先出示机票。随后有3个并发线程,其中两个为条件线程。座位安排是必须始终进行的,而只有当乘客有行李时才要进行行李检查,只有国际航班才有护照检查过程。当3个线程全部完成后,控制结合,指向单一的线程,将机票和登机卡返还乘客。如果某个条件线程没能开始,则会被随后的控制结合点视为已经完成。
图13-58 条件线程
表示法
复杂转换用短粗条表示(表示同步和/或分支的同步条)。可能有一个或多个实线转换箭头从状态指向条棒(该状态为源状态);可能有一个或多个箭头从条棒指向状态(该状态为目标状态)。在条棒边上注明转换标签,用来描述触发事件,监护条件,活动,在转换的条件下解释。每个独立箭头没有各自的转换标识串,它们仅是整个转换中的一部分。
举例
图13-59 比13-55的状态机增加了退出转换。它也表示了从setup状态到各个子域的初始状态的隐式分叉,以及从各子区域的终态到Cleanup状态的隐式结合。
如果当B2状态活动时发生事件f1,则指向Cleanup事件的转换将会发生。该转换是隐式的结合点,它既终结状态B2也终结状态A2。
图13-59复杂转换(分支,结合)
本文地址:http://com.8s8s.com/it/it2085.htm