本节书摘来自异步社区《设计模式解析(第2版•修订版)》一书中的第2章,第2.4节类图,作者【美】Alan Shalloway(艾伦•沙洛维) , James R.Trott(詹姆斯•R.特罗特),更多章节内容可以访问云栖社区“异步社区”公众号查看。
2.4 类图
设计模式解析(第2版•修订版)
基本的建模图
最基本的UML图是类图。它不仅描述了类,而且说明了类之间的关系。这些关系可能有以下这些类型。
当一个类是“一种”另一个类时:is-a(是一种/一个)关系。
当两个类之间存在关联时:
一个类“包含”另一个类:has-a(拥有一个)关系;
一个类“使用”另一个类:use-a(使用一个)关系;
一个类“创建”另一类。
这些类型还有一些变体。比如,说“什么东西包含另一个东西”时,我们可能是指:
被包含者是包含者的一部分(比如汽车中的发动机);
有一个集合,集合中东西可以独立存在(比如机场上的飞机)。
表示类信息的不同方法
第一种情况称为组合(composition),第二种情况称为聚集(aggrega- tion)1。
图2-1说明了重要的几点。首先,矩形表示一个类。在UML中,我可以表示最多三个方面的类的信息:
类名;
类的数据成员;
类的方法(函数)。
表示类的信息有三种不同方式。
最左边的矩形只显示了类名。在不需要更详细信息时,可以使用类的这种表示形式。
中间的矩形显示了类名和类的方法。在本例中,Square类2有一个display方法。display(方法名)前的加号(+)表示此方法是公开的——也就是说,不属于此类的其他对象也可以调用。
最右边的矩形除显示了前面的信息(类名和类的方法)之外,还显示了类的数据成员。在本例中,数据成员length(它是double类型的)前的减号(-)表明这个数据成员的值是私有的,也就是说,除了它所属的对象外,它对其他对象都是不可见的。3
表示访问权限的UML记号
你可以控制类的数据成员和方法成员的可访问性,也可以用UML标记所需要的每个成员的可访问性。大多数面向对象语言中都有如下三种最常见的可访问性。
公开——用一个加号(+)标记。意味着所有对象都可以访问这个 数据或方法。
保护——用一个“井”号(#)标记。意味着只有该类及其所有 派生类(包括其派生类的派生类)可以访问这个数据或方法。
私有——用减号()标记。意味着只有该类的方法可以访问这 个数据或方法。(请注意:某些语言进一步将其限制为特定对象。)
表示关系的UML记号
表示关系的UML记号有如下四种:4
类图还可以表示关系
类图还可以表示不同类之间的关系。图2-2显示了Shape类和它的几个派生类之间的关系。
表示is-a关系
图2-2说明了几件事。首先,Shape类下面的箭头的意思是:指向Shape的那些类派生自Shape类。而且,Shape类的名字是用斜体表示的,说明它是一个抽象类。抽象类就是用来为其派生类定义接口而且存放这些派生类公共数据和方法的类。接口可以看作是没有公共数据和方法的抽象类——它只用来作为一种“为要实现它的那些类的方法进行定义”的方式而已。5
表示has-a关系
如前所述,有两种不同的has-a关系。一个对象可以拥有另一个对象,其中被包含的对象是包含对象的一部分——或者不是。在图2-3中,我表示出Airport“拥有”Aircraft。Aircraft并不是Airport的一部分,但仍然可以说Airport拥有Aircraft,这种关系称为聚集。
聚集
在此图中,我还表示了Aircraft要么是Jet(喷气式飞机),要么是Helicopter(直升飞机)。可以看出Aircraft类是一个抽象类或者接口6,因为它的名字是用斜体表示的。也就是说,Airport可以拥有Jet或Helicopter,但它是以相同方式对待它们的(当作Aircraft)。Airport类右边的空心(未填充的)菱形表示聚集关系。
组合
另一种has-a关系是包含,被包含对象是包含对象的一部分,这种关系也称为组合。
组合和使用
图2-4显示了Car(轿车)拥有Tire(轮胎),后者是它的一部分(也就是说,Car由Tire和其他东西组成),这种has-a关系,称为组合关系(composition),用实心菱形表示。此图上还显示了Car使用了GasStation(加油站)类,这种使用关系用带箭头的虚线表示,也称依赖关系(dependency relationship)。
组合与聚集的异同
组合和聚集都有“一个对象包含一个或多个对象”的意思,但是,组合意味着“被包含对象是包含对象的一部分”,而聚集意味着被包含对象更像是一个集合。我们可以认为组合是一种非共享的关联,被包含对象的生存周期由包含对象控制。适当使用构造函数和析构函数在这里有助于对象的创建和销毁过程。
UML中的注释
在图2-5中有一个新记号:注释。含有“空心菱形表示聚集”信息的方框就是注释。注释记号看上去好像是右角折起的纸。经常能够看到注释通过一条线与特定的类连接起来,表示它只与此类有关。
表示另一个对象所拥有的东西的数量
类图表示的是类之间的关系,但是,对于组合和聚集来说,这两种关系更加关注该类型的具体对象。比如,Airport对象拥有Aircraft对象,但是更具体地说,是特定的机场拥有特定的飞机。于是问题出现了——“一个机场可以拥有多少架飞机呢?”这称为关系的重数(cardinality)。图2-6和图2-7说明了这一点。
重数
图2-6告诉我们,对于一个Airport对象,它可以拥有从0到任意数量(此处用星号表示,但有时候也可以用字母“n”)的Aircraft对象。Airport类旁的“0..1”意味着:对于一个Aircraft对象,它可以被0个或1个Airport对象包含。(0表示它可以在空中飞行,不属于任何机场)。
图2-7告诉我们,对于一个Car对象,它可以拥有4个或5个Tire对象(有或没有备胎),轮胎则只能装在一辆轿车上。我曾听一些人说,如果未指定重数,就意味着只有一个对象,这种想法是不正确的。如果未指定重数,对于对象的数量不应该做任何假设。
虚线表示依赖
和前面一样,图2-7中显示的Car和GasStation之间的虚线表示两者之间存在依赖。UML用带虚线的箭头表示两个模型元素之间的语义关系(意义)。
1 Gamma、Helm、Johnson和Vlissides的《设计模式》一书中将第一种情况称为“聚集”,而将第二种情况称为“组合”(《设计模式》一书中aggregation的确相当于本书中的组合概念,但是该书中的composition则是指对象组合,与继承相对,和本书中的聚集没有关系。——译者注)——正好与UML相反。但是,该书完成于UML标准最终定案之前,事实上书中所给出的定义是与UML一致的。这也说明了开发UML的动机。在UML出现之前已经有好几种各不相同的建模语言,每种都有自己的记号和术语。
2类名在文字中引用时,使用Courier字体表示。
3在一些编程语言中,同类型的对象可以相互共享私有数据。
4原书此段文字与“表示访问权限的UML记号”中的一段文字相同,估计是作者的失误。——译者注
5我知道自己两次使用接口一词,表示的是不同的含义。但是别为此骂我。我也希望对Java和C#的关键字——interface使用另一个名字呢。
6为了简明起见,我不再继续写“抽象类或者接口”了。往后我所称的“抽象类”,均可以视同为“抽象类或者接口”。
本文仅用于学习和交流目的,不代表异步社区观点。非商业转载请注明作译者、出处,并保留本文的原始链接。