类的 UML 画法
从上至下每一方块分别代表类名、属性、方法。
普通类
可见性:+
表示public、#
表示protected、-
表示private
属性(成员变量)表示方式:【可见性】【属性名称】:【属性类型】={属性默认值,为可选}
方法(成员方法)表示方式:【可见性】【方法名称】(【参数名:参数类型,......】): 返回值类型
抽象类
与普通类的不同是,抽象类的类名需要用斜体表示,其虚函数也需要用斜体表示。
类与类之间的关系
继承
用于描述父子类之间的关系,父类又名基类,子类又名派生类。
在语义层面上:A is B,即 HR、Sale、Manager is Employee。
UML 中用带空心三角形的实线来表示。
关联关系
表示一个对象与另一个对象之间有联系。
在 C++ 中这种关联关系在类中是这样体现的,即将一个类的对象作为另一个类的成员变量。
单向关联关系
如果是单向关联,使用的连接线是带单向箭头的实线, 哪个类作为了当前类的成员变量,那么箭头就指向哪个类。
对应图中就是,Condition 知道 MutexLock 的存在,但是 MutexLock 并不知道 Condition 的存在,彼此并不负责对方的生命周期。
在语义层面上:A has B ,即 Condition has MutexLock。
在代码层面上:使用指针或者引用。
双向关联关系
如果两个类互有对方的成员变量,那就用一条直线连接代表双向关系。
对应图中就是,Cumtomer 知道 Order 的存在,但是 Order 也知道 Cumtomer 的存在,彼此并不负责对方的生命周期。
在语义层面上:A has B ,即 Cumtomer has Order。
在代码层面上:使用指针或者引用。
自关联关系
自关联指的就是当前类中包含一个自身类型的对象成员,这在链表中非常常见,单向链表中都会有一个指向自身节点类型的后继指针成员,而双向链表中会包含一个指向自身节点类型的前驱指针和一个指向自身节点类型的后继指针
聚合关系
表示整体与部分的关系。在聚合关系中,成员对象是整体的一部分,但是成员对象可以脱离整体对象独立存在。
表现为整体与局部的关系,但是整体也不会负责局部的销毁。
在语义层面上:A has B ,即 Cumputer has CPU、Memory、MainBoard。
在代码层面上:使用指针或者引用。
组合关系
表示的是一种整体和部分的关系,但是在组合关系中整体对象可以控制成员对象的生命周期,一旦整体对象不存在,成员对象也不存在,整体对象和成员对象之间具有同生共死的关系。
在语义层面上:A has B ,即 Company has HRDepartment、DevelopDepartment、SaleDepartment。
在代码层面上:局部对象。
聚合关系和组合关系的区分
聚合关系的成员对象(局部)的生命周期不归当前类(整体)管,所以采用指针和引用声明成员对象。但是组合关系就不同,当前类(整体)死去会一并带走成员对象(局部),所以这些成员对象也就是属于这个类的局部对象,生命周期和当前类保持一致。
一般来说被组合对象不能脱离组合对象独立存在,而且也只能属于一个组合对象,聚合则不一样,被聚合的对象可以属于多个聚合对象(因为它是引用和指针嘛,但这样我们关系它本身的生命周期问题,因为它具有不确定性了)。
依赖关系
特定事物的改变有可能会影响到使用该事物的其他事物,在需要表示一个事物使用另一个事物时使用依赖关系,大多数情况下依赖关系体现在某个类的方法使用另一个类的对象作为参数。
语义上是一种:A use B 的关系,对应图中的 Tree use Soil Air Water。
在代码上表现为:
- B 作为 A 的成员函数参数
- B 作为 A 的成员函数的局部变量( B 作为 A 的成员函数的返回值)
- A 的成员函数调用 B 的静态方法
关联关系、聚合关系、组合关系之间的区别
聚合和组合的区别我们前面已经讲过,这里我们只要讲清楚聚合和关联的区别即可。
关联的两个对象之间一般是平等的,聚合则一般是不平等的。
最后,再举例子来描述一下这三种关系:
- 朋友之间属于关联关系,因为这种关系是平等的,关联关系只是用于表示两个对象之间的一种简单的联系而已。
- 图书馆看书的时候,人和书属于聚合关系。书是可以独立存在的,而且书不仅可以属于自己,也可以属于别人。
- 人和自己的心脏属于组合关系,因为心脏不能脱离人体而独自存在。
总结
- 继承关系是一个垂直的关系,其他四种是横向关系
- 从语义层面:继承 is;依赖:use;关联、聚合、组合:has
- 耦合程度: 依赖 < 关联 < 聚合 < 组合 < 继承
- 代码层面:依赖:关注的是成员函数;关联、聚合、组合:关注的是数据成员; 继承:既有数据成员也有成员函数。