合成/聚合复用原则 (Composite/Aggregate Reuse Principle)
定义:在软件复用时,先考虑使用合成或者聚合等关联关系来实现,其次考虑使用继承关系来实现。
合成和聚合的区别
- 聚合:用来表示“拥有”关系或者整体与部分的关系。代表部分的对象有可能会被多个代表整体的对象所共享,而且不一定会随着某个代表整体的对象创建而创建,销毁而销毁。部分的生命周期可以超越整体。
例如:会员和门店的关系,当门店被删除后,会员还可以被公司引用;当 Counter 类创建对象时,不一定会生成 Member 对象
/**
* 会员
*/
public class Member {
}
/**
* 门店
*/
public class Counter {
private Member member;
public Counter(Member member){
this.member = member;
}
}
/**
* 公司
*/
public class Company{
private Member member;
public Company(Member member){
this.member = member;
}
}
- 合成:用来表示一种强得多的“拥有”关系。一个合成关系中的成分对象是不能与另一个合成关系共享的,而且会随着合成的新对象的创建而创建,销毁而销毁。也就是说成分对象和新对象的生命周期是一样的。
例如:一个人由头、四肢和各种器官组成,人与这些具有相同的生命周期;下面代码中,当 People 类创建对象时,也一定会生成 Heart 对象
/**
* 心脏
*/
public class Heart {
}
/**
* 人
*/
public class People {
private Heart heart;
public People() {
this.heart = new Heart();
}
}
通常类的复用分为继承复用和合成/聚合复用两种
-
合成/聚合复用:将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能
优点 :
1、维持了类的封装性:被纳入对象的内部细节是新对象看不见的,这种复用又称为“黑箱”复用。
2、新旧类之间的耦合度低:这种复用所需的依赖较少,新对象存取成分对象的唯一方法是通过成分对象的接口。
3、复用的灵活性高:这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。 -
继承复用:用继承方式实现复用,简单和易实现
缺点:
1、破坏了类的封装性:继承会将父类的实现细节暴露给子类,父类对子类是透明的,这种复用又称为“白箱”复用。
2、子类与父类的耦合度高:父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。
3、限制了复用的灵活性:从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。
由于继承复用有以上的缺点,所有尽量使用合成/聚合而不是继承来达到对实现的复用,是非常重要的设计原则。 -
Is-A 与 Has-A
Is-A:( 是 “A” 小明是人类)表示的是属于得关系。比如兔子属于一种动物(继承关系)。
Has-A:( 有 “A” 汽车有轮胎) 表示组合,包含关系。比如兔子包含有腿,头等组件;就不能说兔子腿是属于一种兔子(不能说是继承关系)