在设计的时候多多少少都避免不了接触UML设计图。我们在此简单的介绍下UML设计图的相关内容。
UML设计图的关系
- 依赖(Dependency)
- 关联(Association)
- 聚合(Aggregation)
- 组合(Componsition)
- 泛化(Generalization)
1. 依赖
依赖用虚线箭头表示。
依赖表明的是一种松耦合的关系。一般作为某个成员函数的返回值、形参、方法局部变量等的调用。
比如:
public class Student {
// 形参构成依赖
public void useComputer(Computer computer) {
...
}
// 返回值构成依赖
public Computer myComputer() {
...
}
}
2. 关联
关联关系用实线加箭头表示,类之间的关系比依赖要强。学生与老师是关联的,学生可以不用电脑,但是学生不能没有老师。如下图所示:
关联与依赖的对比:
相似之处:
关联暗示了依赖,二者都用来表示无法用聚合和组合表示的关系。
区别:
(1)发生依赖关系的两个类都不会增加属性。其中的一个类作为另一个类的方法的参数或者返回值,或者是某个方法的变量而已。
发生关联关系的两个类,类A成为类B的属性,而属性是一种更为紧密的耦合,更为长久的持有关系。
(2)从关系的生命周期来看,依赖关系是仅当类的方法被调用时而产生,伴随着方法的结束而结束。关联关系当类实例化的时候产生,当类对象销毁的时候关系结束。相比依赖,关联关系的生存期更长。
关联关系有单向关联、双向关联、自身关联、多维关联等等。其中后三个可以不加箭头。
单向关联
双向关联
自身关联
多维关联
3. 聚合
聚合关系使用实线加空心菱形表示。聚合用来表示集体与个体之间的关联关系。例如班级与学生之间存在聚合关系,类图表示如下:
public class Classes {
public Student student;
public Classes(Student student) {
this.student = student;
}
}
4. 组合
复合关系使用实线加实心菱形表示。组合又叫复合,用来表示个体与组成部分之间的关联关系。例如学生与心脏之间存在复合关系,类图表示如下:
public class Student {
public Heart heart;
public Student() {
this.heart = new Heart();
}
}
(1)聚合关系没有组合紧密。
学生不会因为班级的解散而无法存在,聚合关系的类具有不同的生命周期;而学生如果没有心脏将无法存活,组合关系的类具有相同的生命周期。
这个从构造函数可以看出。聚合类的构造函数中包含另一个类的实例作为参数,因为构造函数中传递另一个类的实例,因此学生可以脱离班级体独立存在。组合类的构造函数包含另一个类的实例化。因为在构造函数中进行实例化,因此两者紧密耦合在一起,同生同灭,学生不能脱离心脏而存在。
(2)信息的封装性不同。
在聚合关系中,客户端可以同时了解Classes类和Student类,因为他们是独立的。
在组合关系中,客户端只认识Student类,根本不知道Heart类的存在,因为心脏类被严密地封装在学生类中。
理解聚合与复合的区别,主要在于聚合的成员可独立,复合的成员必须依赖于整体才有意义。
泛化
泛化是学术名称,通俗来讲,泛化指的是类与类之间的继承关系和类与接口之间的实现关系。
继承关系使用直线加空心三角形表示。类图结构如下:
类接口的实现关系使用虚线加空心三角形表示。类图结构如下:
讨论:
关于关联,聚合,组合之间的关系我们需要特殊说明下。关联主要代表的是同级别的内容,比如人和电脑。而聚合和组合之间则表示的是整体和部分的逻辑。而组合的耦合关系是要紧于聚合的。比如我们在聚合的例子中(Student和Class的关系),Student是作为Class的一个构建函数入参来构建的。而在组合的关系中(Student和Heart的关系), Heart这个成员变量是在构建函数中new出来的。所以Heart的生命周期和Student的生命周期是一致的。