在UML类图中,类与类之间有几种常见关系:依赖、关联、聚合、组合、泛化。
1、依赖(Dependency)
依赖是一种很有用的关系,它用来表述一个类A“use”了另一个类B。A可以是通过任何方式“use”类B,如:
1)A的成员函数的返回值为B;
2)A的成员函数使用B作为函数参数;
3)A的成员函数的内部实现使用了B;
依赖关系通常使用虚线箭头“---->”表示,箭头指向被“use”的类。
在C++代码中,依赖关系是这样对应的:
class A
{ public: B returns_a_B();
void has_a_B_argument(B);
void has_a_B_in_its_implementation();
};
A::void has_a_B_in_its_implementation(){ B b; }
2、关联(Association)
关联可以表述成一个类“知道”另一个类。如何“知道”呢?在C++中,类A“知道”类B一般是通过指针实现的(也可以使用引用或者值),即类A有一个成员变量是指向B的指针(或者引用、值)。
关联可以分为双向关联、单向关联、自身关联。
1)双向关联
双向关联A-B:双方都“知道”对方,都可以调用对方的公共属性和方法。
对应的C++代码为:
class A {
public:
B* pB;
};
class B {
public:
A* pA;
};
2)单向关联
单向关联A->B:表示A“知道”B,A可以调用B的公共属性和方法。没有生命周期的依赖。
对应的C++代码为:
class A {
public:
B* pB;
};
class B {
};
3)自身关联
自身关联:自己引用自己,这个在链表中非常常见。
可以看到,上面的Object类,就是一个自身关联的应用,它有一个自己指向自己的指针,用来实现链表。
对应的C++代码为:
class Object {
public:
int data;
Object* next;
};
class ObjectList {
public:
Object* first;
ObjectList();
void insert(Object* obj);
void print() const;
};
3、聚合(Aggregation)和组合(Composition)
聚合和组合都是用在表述整体-部分关系的时候,二者只是在生命周期问题上有差异。
1)聚合通常可以理解成“has a”关系。如果类A聚合类B,那么类A“has a”类B,同时,在A的生命周期结束后类B必须依然存在或者有意义。比如房间有一张桌子,那么房间和桌子的关系就是聚合:即使房间没有了,那张桌子还是存在的,桌子是可以脱离房间而存在的。
对应的C++代码:
class Table {
};
class Room {
public:
Table aTable;
};
2)组合通常可以理解为“is a part of”。和聚合不同的是,如果类A组合类B,那么当A生命周期结束后,类B也随之结束,也就是说B不能脱离类A而存在。就如同鸟都有两只翅膀一样,当鸟消失了,翅膀也随之不存在了。
对应的C++代码:
class Wing {
};
class Bird{
public:
Wing leftWing;
Wing rightWing;
};
可以发现,如果单纯从C++代码来看,聚合关系和组合关系没有什么不同,要区分聚合和组合,只能从语义分析。
补充:
组合关系还有另一层含义:“is a”。不过这种含义,仅仅用来角色方面,即“is a”角色。比如一个人,是丈夫角色。那也可以看做组合;手机可以看做“Camera”、“Music Player”等。
从上图我们可以看出,Battery和Smart Phone是聚合关系,因为电池是手机的一部分,但是电池可以脱离手机而存在。而IMEI Number和Smart Phone是组合关系,一般情况下一个Smart Phone只有一个IMEI Number,手机消失后,IMEI跟着消失。而我们知道现在的手机可以拍照、上网、播放音乐,因此手机可以扮演相机、web冲浪、音乐播放器的角色,所以Smart phone和Camera、Web Browser、Music Player是组合的关系。
4、泛化(Realization)
泛化关系也被常用作继承(inherit)关系,是用来表述“Is-a”这种关系的,比如Car和Police-Car的关系,Police-Car “is a” Car。
对应的C++代码为:
class Car {
};
class Police_Car{
};