第三章 类图:基本概念
类图可用来描述系统中各种对象的类型,也可描绘出对象间各种各样的静态关系。此外,类图中也可以秀出类的性质(property)与操作(operation),以及可应用到对象间连接方式的一些限制(constraint)。在UML中,我们用特性(feature)来代表累的性质与操作这两种概念。
1、性质
性质代表类的结构特性(structural feature)。虽然只是一个概念,不过它却可以用两种非常不同的表示法来呈现:属性与关联。
2、属性
属性(attributes)表示法用类别四方形中的一行文字来表示性质。完整语法如下:
可见性 名称 : 类型 多重性 = 预设值 { 性质 - 字符串 }
如:
-name:string [1] = "Untitled"{ readonly }
在整个语法当中,只有【名称】部分是必要的。
(1) 可见性标志代表这个属性是public、private或protect。
(2) 属性的名称大约等同于程序语言中的名称 -它说明类如何参考这个属性的。
(3) 属性的类型代表一种限制条件,说明属性中究竟可放入哪一种对象。
(4) 关于多重性(multiplicity),稍后再解释它。
(5) 预设值(default value)说明在新产生的对象中,如果产生过程中没有指定值给这个属性,那么我们就会把这个值指定给属性。
(6){性质 - 字符串}允许你指明这个属性的一些额外性质。
3、关联
性质的另一种表示法是关联。属性中可表示出来的信息,差不多也都可以再关联中表示出来。
关联用两个类别中的实线来表示它,箭头会有来源类指向目标类。性质的名称会出现在关联的目标端,同时还会加上它的多重性。关联的目标端所连接的类就是这个性质的形态。
一般来说,倾向于把属性用在小的东西上,例如日期或布尔值等,而且他们通常都是值形态的东西。另一方面,我会把关联用在有显著意义的类上,例如客户或订单。同时,也倾向于用类四方形画出图种有显著意义的类,这也会导致去用关联;另一方面,我会把不重要的东西表示成属性。做出这种选择,目的主要是为了要强调出重点。
4、多重性
性质的多重性代表它可能会有多少个对象存在。比较一般的说法是我们可以用一个上限与一个下限来定义多重性。属性的预设多重性是【1】。虽然UML的超模型中有这条规则存在,不过如果图中某个属性没有标识出多重属性的话,我们不能因此就假设它的多重性为【1】,因为多重性信息可以隐藏起来。
5、双向关联
双向关联代表一对性质,它们从不同方向彼此连接在一起。如下图:
Car类中有性质owner:Person[1],而Person类则有性质cars:Car[*]。
6、操作性
操作是类知道该如何达成的一些动作。很明显地,操作就是类中的方法(method)。
在UML中,操作的完整语法为:
可见性 名称(参数列表) : 返回类型 { 性质 -字符串 }
(1) 可见性是public、private或protect
(2) 名称是一个字符串
(3) 参数列表是操作会用到的一串参数
(4)返回类型
(5)性质-字符串代表可应用到此操作的一些性质值。
参数列表中的参数是用跟属性类似的方式来表示的。其语法如下:
方向性 名称 : 形态 = 预设值
(1) 名称、形态与预设值跟属性中的都一样。
(2)方向性代表参数是用来输入(in)、输出(out)或输出入(inout)用的。
评注:把会改变跟不会改变类中状态的操作区分开来是非常有益的事。
UML中将query(查询)定义成:只会从类中取出值、不会改变系统的状态的操作。也就说,它不会产生副作用。你可以吧这种操作加上{query}性质字符串。还有,我自己喜欢将会改变状态的操作加上modifiers(修改)性质字符。
操作指的是我们可呼出对象的某种东西-它代表程序宣告,而方法则是程序本体。这两种东西在多型存在时,会有很大差异。例如:如果你有一个基类和三个子类,而每个子类都覆盖基类的getPrice操作。这时候,我们就有一个操作,四个方法。
7、一般化关系
对一般化关系明显解释就是继承(inheritance)。在主流的面向对象语言中,子类会继承超类(superclass)的所有类特性,而且还可能会覆盖任何超类的方法。
8、相依性
如果我们该某个元素的定义可能会影响到其他元素,那么这两个元素间就存在着相依性。对类来说相依性存在的原因有好几个:某个类会传送讯息给另一个类;某个类吧另一个类当成一部分资料;某个类把另一个类当成方法中的参数等。
如果系统不断成长,你必须越来越担心该任何去控制相依性。如果你没办法控制相依性的话,那么我们系统所做的任何改变都会造成涟漪效应,让越来越多东西跟着改变。
表1 本书精心挑选出来一些有用的相依性关键字:
关键字 | 意义 |
call | 来源元素回去调用目标元素的某个操作 |
create | 来源元素会产生目标元素的实例 |
derive | 来源元素是由目标元素衍生出来的 |
instantiate | 来源元素是目标元素的实例 |
permit | 目标元素允许来源元素去存取目标元素中的私有特性。 |
realize | 来源元素是目标元素中所定义规格或接口的实现 |
refine | 修饰代表不同语义等级间的关系。 |
substiute | 来源元素是可替代目标元素的 |
trace | 我们用这个相依性来追踪一些东西,例如从需求到类的相依性,或某个模型中的变动是如何练接到其他模型中变动的。 |
use | 来源元素在实现时需要用到的目标元素 |
一般性原则是尽量降低相依性,特别是相依性会泛滥到系统的一大片区域时。还有一点要注意的是:我们应该小心避免循环式相依性,因为它会导致循环性的变动。
9、限制规则
UML允许我们用任何东西来描述限制。唯一的规则是:请把限制放在大括号{}内。
有一种选择性的做法是:替限制命名时,把名称放在最前面,后面紧接着一个冒号,像这样{不允许乱伦:夫妻双方不能是兄弟姐妹}
10、何时使用类图
类图是UML的主干,因此你可以发现随时都得到它们。
类别图的最大麻烦在于里面包含太多东西了。所以它们可能会被过度使用。下面是一些使用上的提示:
A、不要试着用到所有可以使用的表示法。先简单用本章所提及的一些东西:包括类别、关联、属性、一般化关系与限制等。至于高级的东西,只在你觉得有需要时再去用它们。
B、我发现到概念性的类图在探索某个企业的领域语言时特别有用。想要这么做时,你必须努力保持不去讨论软件本身,而且尽可能使用非常简单的表示法。
C、不要画出包含所有东西的模型。相反地,把重心放在关键地方。持有一些常用的到、时时更新的图,比起持有很多被人遗忘掉、过时的图还好。
使用类图的最大危险在于你可能会过度把焦点放在结构上,而忽略系统中的行为。