UML精粹3 - 类图,序列图,CRC
类图Class diagram
类图描述系统中的对象类型,以及它们之间的各种静态关系。类图也展示类的性质和操作,以及应用于对象连接方式的约束。UML中的特性feature,涵盖了性质property和操作operation。
性质property
性质可以以两种方式出现:属性attribute和关联association。属性是类方框内的一行文本(语法“visibility name: type multiplicity = default {property-string}”, 其中访问级别public为+,private为-),关联是一根两个类之间的实线,方向从源到目标类,性质的名称及多重性放在关联的目标端。例如
性质表示为属性:
性质表示为关联:
性质一般解释为编程语言中的字段。
多重性
略。
双向关联
双向关联是一对性质,它们从两个方向连接在一起。例如,Car有性质owner,Person有性质cars。使用双向箭头更容易突出双向连接的关系。编程语言实现双向关联关系的维护比较复杂,一般在关联的一端维护,如果是1对多的关联,在多的这一端维护。
或者
操作operation
操作是类知道如何执行的动作,对应于类中的方法。
操作的语法:
visibility name (parameter-list) : return-type {property-string}
操作和方法method:操作是对象上可以调用的某些东西--例程申明--而方法是例程体。在多态情况下,两者是不同的。如果一个超类型有3个子类,每个子类都覆盖了超类型的getPrice操作,你有一个操作和4个实现它的方法。当然,人们通常会混用这两个概念。
泛华generalization
。略。
注解符和注释
。略。
依赖dependency
如果改变一个元素(supplier)的定义会导致改变其它类(client),那么两个元素之间就存在依赖。依赖的存在有各种原因:一个类发送消息给另一个类;一个类拥有另一个类作为其数据的一部分;一个类把另一个类作为操作的参数。只有当依赖直接和你要沟通的特定主题相关时,才有选择地展示它们。为了理解和控制依赖,你最好只在包图上使用依赖。
约束规则
画类图时,你正在做的大部分事情都是表明约束。关联、属性和泛华的基本构造已经描述了许多重要的约束,但还是有不能表达每一个约束。UML运行你使用任何形式来描述约束,唯一的规则是要把它们放进花括号中。例如,{不允许乱伦:丈夫和妻子不能是兄弟、姐弟}
何时使用类图。
类图是UML的骨架,因此你会发现你自己总是用到它。类图的最大危险是你可能只聚焦于结构,而忽略了行为。因此,画类图的时候,总是要结合某些形式的行为技术。
序列图Sequence diagram
交互图(interaction diagram)描述对象组在某些行为中如何协作。序列图是最常见的交互图,通常用来捕获单个场景的行为。下面是两张计算订单价钱的序列图,一张采用中央控制的方式,由Order来控制计算的过程;一张采用分布控制的方式,每个参与的对象完成一点计算,最后得到结果。序列图可以清晰地指出参与者交互的差异。名词解释:参与者participant,参与交互的对象;寻获消息found message,第一个消息,来自一个不确定的源;生命线lifeline,对象存活时间;激活activation,对象在交互中处于活动状态。
采用中央控制的时序图
采用分布控制的时序图
- 创建和删除参与者。下图展示序列图如何表示对象的创建和删除。
循环,条件等
下图是序列图表示循环和条件的一个例子,注意这不是序列图的强项,最好使用活动图或者代码本身来展示。把序列图当做对象如何交互的可视化,而不是展示建模控制逻辑的方式。
下面是对应的伪代码:
循环和条件都使用交互框interaction frame,框包含序列图的某些区域,可以划分成多个片断。每个框有一个操作符,每个片断可以有一个警戒条件。常见操作符有:
- alt,多选一的片断
- opt,可选的,等同于只有一个片断的alt
- par,并行
- loop,循环
- region,关键区域;片断一次只有一个线程执行
- neg,否定,片断展示无效的交互
- ref,引用;应用到另一张图中定义的交互。
- sd,序列图;
交互框是UML2新加的功能,在UML1使用迭代标记(iteration marker)和警戒条件(guard)来表示循环和条件。
同步和异步
UML2使用实心箭头表示同步消息调用,条形箭头表示异步消息调用;在这之前,人们也用半箭头表示异步调用,全箭头表示同步。
何时使用序列图
当要查看单个用例内的若干个对象的行为时,你应该使用序列图。序列图擅长展示对象之间的协作,不大擅长于精确定义行为。
如果你要查看跨越多个用例的单个对象的行为,使用状态图。如果你要查看跨越许多用例或许多线程的行为,考虑活动图。
如果你要快速探索多个多选一的交互,最好使用CRC卡,这样可以避免许多绘制和擦除的工作。使用CRC卡探索,然后使用序列图捕获定下来的交互。
CRC(Class Responsibility Collaboration,类-职责-协作)卡
下面是一个CRC卡的样例。
CRC式思考的一个重要部分是识别责任。一项责任是一个短句子,概况了对象应该做的事情:对象执行的动作、对象维护的一些知识或者对象做的一些重要决定。思路是,你应该能够拿起任何类,用一些责任来概括它。这样做能够帮助你清晰地思考类的设计。
协作者collaborator指需要和这个类一起工作的其它类,这样能帮助你思考类之间的链接。
注意不要列举大量的低级别的责任,这样容易迷失目标。
类图:进阶概念
关键词
使用关键词来标记一个符号,例如<<interface>>或者{interface}。
责任
描述类的责任。
静态操作和属性
使用下划线表示静态。
聚合aggregation和组合composition
一般来说,聚合和关联没有什么区别,组合强调对象的独占关系。
聚合的例子
组合的例子
派生性质derived property
可以基于其他值计算出来。在前面加上个斜杠/表示。
接口和抽象类
一个接口和抽象类的Java例子
使用小球-球窝表示法(UML2引进)
使用棒棒糖表示法(人们也经常使用的方法)
只读和冻结。使用关键字来描述{readonly},{frozen}。
引用对象和值对象。{value}
限定关联qualified association
限定关联是UML里关于数组、图、哈希和词典这些编程概念的等同物。
下图使用限定符表示Order和Order Line类之间的关联,说明在和一个Order的关联中,对应每一个Product实例,可能有一个Order Line。
从软件的角度看,限定暗示以下接口:
这样,到给定Order Line的所有访问都需要一个Product作为参数,说明这是一个使用键/值数据结构的实现。
在概念建模中,只在为了展示约束“在每个Order上每个Product有单个Order Line”时,才使用限定符构造关联。
分类classification和泛华generalization
人们经常谈论说类型化是一个is-a关系,但是要小心,is-a还可以有不同的含义。考虑下面的语句。
- Shep是一只博德牧羊犬
- 一只博德牧羊犬是一只犬
- 犬是动物
- 一只博德牧羊犬是一个品种
- 犬是一个物种
现在试着组合语句。如果结合语句1和2,得到“Shep是一只犬”;结合语句2和3,得到“博德牧羊犬是动物”;结合1、2、3,得到“Shep是一只动物”。到目前为止,一切都好。现在尝试结合1和4,得到“Shep是一个品种”;结合2和5,得到“博德牧羊犬是一个物种”。这些就不那么合适了。
为什么会这样呢?原因是一些是分类(对象Shep是类型博德牧羊犬的一个实例),一些是泛华(类型博德牧羊犬是类型犬的子类型)。泛华是传递性的,分类不是。可以在分类后跟着泛华,但反之不行。
要小心is-a,不要不合适地使用子类型化以及混淆责任。这个例子中,更好的子类型化测试是语句“犬是一种动物”和“每个博德牧羊犬的实例是犬的实例”。
UML使用<<instantiate>>关键字的关联表示分类。
下面的内容略
- 多重和动态分类。
- 关联类。
- 模板(参数化)类。
- 枚举
- 主动类
- 可见性
- 消息