UML 类关系--依赖、关联、聚合、组合、泛化

在学习面向对象设计时,类关系涉及依赖<关联<聚合<组合<泛化这五种关系,耦合度依次递增。关于耦合度,可以简单地理解为当一个类发生变更时,对其他类造成的影响程度,影响越小则耦合度越弱,影响越大耦合度越强。

参考博文《设计模式中类的关系》,书上对这四种关系的定义:

  • 依赖(Dependency)关系是类与类之间的联接。依赖关系表示一个类依赖于另一个类的定义。例如,一个人(Person)可以买车(car)和房子(House),Person类依赖于Car类和House类的定义,因为Person类引用了Car和House。与关联不同的是,Person类里并没有Car和House类型的属性,Car和House的实例是以参量的方式传入到buy()方法中去的。一般而言,依赖关系在Java语言中体现为局域变量、方法的形参,或者对静态方法的调用。
  • 关联(Association)关系是类与类之间的联接,它使一个类知道另一个类的属性和方法。关联可以是双向的,也可以是单向的。在Java语言中,关联关系一般使用成员变量来实现。
  • 聚合(Aggregation) 关系是关联关系的一种,是强的关联关系聚合是整体和个体之间的关系。例如,汽车类与引擎类、轮胎类,以及其它的零件类之间的关系便整体和个体的关系。与关联关系一样,聚合关系也是通过实例变量实现的。但是关联关系所涉及的两个类是处在同一层次上的,而在聚合关系中,两个类是处在不平等层次上的,一个代表整体,另一个代表部分。
  • 组合(Composition) 关系是关联关系的一种,是比聚合关系强的关系。它要求普通的聚合关系中代表整体的对象负责代表部分对象的生命周期,组合关系是不能共享的。代表整体的对象需要负责保持部分对象和存活,在一些情况下将负责代表部分的对象湮灭掉。代表整体的对象可以将代表部分的对象传递给另一个对象,由后者负责此对象的生命周期。换言之,代表部分的对象在每一个时刻只能与一个对象发生组合关系,由后者排他地负责生命周期。部分和整体的生命周期一样

一、依赖(Dependency)

依赖关系使用虚线加箭头表示,如下图所示:

UML 类关系--依赖、关联、聚合、组合、泛化_第1张图片


学生在学习生活中经常使用电脑,于是对电脑产生了依赖。依赖关系是五种关系中耦合最小的一种关系。类A要完成某个功能引用了类B,则类A依赖类B。依赖在代码中主要体现为类A的某个成员函数的返回值、形参、局部变量或静态方法的调用,则表示类A引用了类B

class Car {
	public static void run(){
		System.out.println("汽车在奔跑");
	}
}
 
class Driver {
	//使用形参方式发生依赖关系
	public void drive1(Car car){
		car.run();
	}
	//使用局部变量发生依赖关系
	public void drive2(){
		Car car = new Car();
		car.run();
	}
	//使用静态变量发生依赖关系
	public void drive3(){
		Car.run();
	}
}


二、关联(Association)

关联关系在java中一般使用成员变量来实现,有时也用方法形参的形式实现。依然使用Driver和Car的例子,使用方法参数形式可以表示依赖关系,也可以表示关联关系,毕竟我们无法在程序中太准确的表达语义。在本例中,使用成员变量表达这个意思:车是我自己的车,我“拥有”这个车。使用方法参数表达:车不是我的,我只是个司机,别人给我什么车我就开什么车,我使用这个车。
如下图所示:

UML 类关系--依赖、关联、聚合、组合、泛化_第2张图片
 

class Driver {
	//使用成员变量形式实现关联
	Car mycar;
	public void drive(){
		mycar.run();
	}
	
	//使用方法参数形式实现关联
	public void drive(Car car){
		car.run();
	}
}

相似之处:关联暗示了依赖,二者都用来表示无法用聚合和组合表示的关系。

区别:
(1)发生依赖关系的两个类都不会增加属性。其中的一个类作为另一个类的方法的参数或者返回值,或者是某个方法的变量而已。发生关联关系的两个类,类A成为类B的属性,而属性是一种更为紧密的耦合,更为长久的持有关系。 
(2)从关系的生命周期来看,依赖关系是仅当类的方法被调用时而产生,伴随着方法的结束而结束。关联关系当类实例化的时候产生,当类对象销毁的时候关系结束。相比依赖,关联关系的生存期更长。

关联关系有单向关联、双向关联、自身关联、多维关联等等。


三、聚合(Aggregation)


聚合关系使用实线加空心菱形表示。聚合用来表示集体与个体之间的关联关系。例如班级与学生之间存在聚合关系,类图表示如下:

UML 类关系--依赖、关联、聚合、组合、泛化_第3张图片


聚合关系是是一种比较强的关联关系,java中一般使用成员变量形式实现。对象之间存在着整体与部分的关系。代码如下:

class Driver {
	//使用成员变量形式实现聚合关系
	Car mycar;
	public void drive(){
		mycar.run();
	}
}

四、组合(复合,Composition)

复合关系使用实线加实心菱形表示。组合又叫复合,用来表示个体与组成部分之间的关联关系。例如学生与心脏之间存在复合关系,类图表示如下:

UML 类关系--依赖、关联、聚合、组合、泛化_第4张图片


组合关系在代码上与关联关系表现一致,类Heart将成为类Person的成员变量。比如

class Person{
    //使用成员变量形式实现组合关系
   Heart myHeart;

    // Heart和Person同生命周期
   public Driver(Heart heart){
	 this.myHeart= heart;
	}
}

聚合与组合的对比:
(1)聚合关系没有组合紧密。
学生不会因为班级的解散而无法存在,聚合关系的类具有不同的生命周期;而学生如果没有心脏将无法存活,组合关系的类具有相同的生命周期。

这个从构造函数可以看出。聚合类的构造函数中包含另一个类的实例作为参数,因为构造函数中传递另一个类的实例,因此学生可以脱离班级体独立存在。组合类的构造函数包含另一个类的实例化。因为在构造函数中进行实例化,因此两者紧密耦合在一起,同生同灭,学生不能脱离心脏而存在。

(2)信息的封装性不同。
在聚合关系中,客户端可以同时了解Classes类和Student类,因为他们是独立的。在组合关系中,客户端只认识Student类,根本不知道Heart类的存在,因为心脏类被严密地封装在学生类中。理解聚合与复合的区别,主要在于聚合的成员可独立,复合的成员必须依赖于整体才有意义。

五、泛化(Generalization)

泛化是学术名称,通俗的来讲,泛化指的是类与类之间的继承关系和类与接口之间的实现关系。

继承关系使用直线加空心三角形表示。
类接口的实现关系使用虚线加空心三角形表示。

UML 类关系--依赖、关联、聚合、组合、泛化_第5张图片


小结:依赖、关联、聚合、组合与泛化代表类与类之间的耦合度依次递增。

  • 依赖关系比较好区分,它是耦合度最弱的一种,在编码中表现为类成员函数的局部变量、形参、返回值或对静态方法的调用。
  • 关联、聚合与组合在编码形式上都以类成员变量的形式来表示,所以只给出一段代码我们很难判断出是关联、聚合还是组合关系,我们需要从上下文语境中来判别。
  • 关联表示类之间存在联系,不存在集体与个体、个体与组成部分之间的关系。
  • 聚合表示类之间存在集体与个体的关系。
  • 组合表示个体与组成部分之间的关系。
  • 依赖、关联、聚合与组合是逻辑上的关联,泛化是物理上的关联。物理上的关联指的是类体的耦合,所以类间耦合性最强。

参考文献:https://blog.csdn.net/zhengzhb/article/details/7190158
[1] 认识UML中类之间的依赖、关联、聚合、组合、泛化的关系
[2] UML类关系(依赖,关联,聚合,组合的区别)
[3] 谈一谈自己对依赖、关联、聚合和组合之间区别的理解

你可能感兴趣的:(设计模式)