合成/聚合复用原则经常又叫做合成复用原则。合成/聚合复用原则就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分:新的对象通过向这些对象的委派达到复用已有功能的目的。简短的表述是:要尽量使用合成/聚合,尽量不适用集成。
一、合成和聚合的区别
合成和聚合均是关联关系的特殊种类。聚合用来表示“拥有”关系或者整体与部分的关系;而合成则用来表示一种强的多的“拥有”关系。在一个合成关系里,部分和整体的生命周期是一样的。一个合成的新的对象完全拥有对其组成部分的支配权,包括他们的创建和湮灭等。使用程序语言的术语讲,组合而成的新对象对组成部分的内存分配、内存释放有绝对的责任。
更进一步讲,一个合成的多重性不能超过1,换言之,一个合成关系中的成分对象是不能与另一个合成关系共享的。一个成分对象在同一个时间内只能属于一个合成关系。如果一个合成关系湮灭了,那么所有的成分对象要么自己湮灭所有的成分对象,那么就得将这一责任交给别人。
二、复用的基本种类
在面向对象的设计里,有两种基本的办法可以在不同的环境中复用已有的设计和实现,即通过合成/聚合或通过继承。这两种不同的复用方式在可维护性上有何区别呢?
1、合成/聚合复用
由于合成或聚合可以将已有的对象纳入到新的对象中,使之成为新对象的一部分,因此新的对象可以调用已有的对象功能。这样做有如下好处:
- 新对象存取成分对象的唯一方法是通过成分对象的接口。
- 这种复用是黑箱复用,因为成分对象的内部细节是新对象所看不见的。
- 这种复用支持包装。
- 这种复用所需的依赖较少
- 每一个新的类可以将焦点集中在一个任务上
- 这种复用可以在运行时间内动态进行,新对象可以动态的引用与成分类型相同的对象。
一般来说,如果一个角色得到了更多的责任,那么可以使用合成/聚合关系将新的责任委派到合适的对象。当然,这种复用也有缺点。最主要的缺点就是通过使用这种复用构造的系统会有较多的对象需要管理。
2、通过继承达到复用
合成/聚合可以应用到几乎所有的环境中,而继承只能应用到很有限的一些环境中。并且我们是使用过程中,首先需要考虑合成/聚合,而不是继承。
继承复用通过扩展一个已有对象的实现来得到新的功能,基类明显的捕获共同的属性和方法,而子类通过增加新的属性和方法来扩展超类的实现。继承是类型的复用。
①继承复用的优点:新的实现较为容易,因为超类的大部分功能可以通过继承关系自动进入子类;修改或扩展继承而来的实现较为容易。
②继承复用的缺点:继承复用破坏包装,因为继承将超类的实现细节暴露给子类。由于超类的内部细节常常是对子类型透明的,因此这种复用是透明复用;如果超类发生改变,那么子类的实现也不得不发生改变。这种改变具有传递性,将一级一级的传递下去;从超类继承而来的实现是静态的,不可能在运行时间内发生改变,因此没有足够的灵活性。
三、区分Has-A和Is-A
Is-A是严格的分类学意义上的定义,意思是一个类是另一个类的“一种”。而Has-A则不同,他表示一个角色具有某一项责任。 导致错误的使用继承而不是合成/聚合的一个常见原因是错误的把Has-A当做Is-A。Is-A代表一个类是另一个类的一种;Has-A代表一个类是另一个类的一个角色,而不是另一个特殊种类。
本文通过总结《java与模式》而来。