合成复用原则
合成复用原则(Composition /Aggregate Reuse Principle)
合成复用原则又称为组合/聚合复用原则。
优先使用对象组合,而不是通过继承来达到复用的目的。
What:什么是合成复用原则
在一个新的对象里通过关联关系(包括组合关系和聚合关系)来使用已有的对象,使之成为新对象的一部分。新对象通过调用已有对象的方法达到复用功能的目的。简而言之,在复用时要尽量使用组合/聚合关系(关联关系),少用继承。
Why:为什么要使用合成复用原则
在面向对象设计中主要有两种方式复用已有的设计和实现,第一种是通过合成复用原则,另外一种是通过继承。但是应该首先考虑使用组合/聚合。使用组合/聚合原则可以使系统更加灵活,降低类与类之间的耦合性,一个类的变化对其他类造成的影响相对较少。
1、由于组合聚合关系可以将已有的对象纳入到新对象中,使之成为新对象的一部分,因此新对象可以调用已有对象的功能。这样做可以使成员对象的内部实现细节对于新对象不可见。
2、相对于继承关系而言,耦合度较低,成员对象的变化对新对象的影响不大,可以在新对象中根据实际需要有选择性地调用成员对象的操作。
3、合成复用原则可以在运行时动态进行,新对象可以动态的引用于成员对象类型相同的其他对象。
通过继承来进行复用的主要问题:
1、继承复用会破坏系统的封装性,因为继承会将基类的实现细节暴露给子类,基类的某些内部细节对于子类来说是可见的。如果基类发生改变,子类的实现也不得不发生改变。
2、从基类继承而来的实现是静态的,不可能在运行时发生改变,没有足够的灵活性。
3、而且继承只能在有限的环境中使用。
How:怎么使用组合/聚合原则
一般来说,如果两个类之间是Has-A的关系应该使用组合/聚合,如果是Is-A关系可以使用继承。
示例
某公司开发人员在初期的CRM系统设计中考虑到客户数量不多采用Access作为数据库,与数据库操作有关的类如CustomerDAO类等都需要连接数据库,连接数据库的方法getConnection()封装在DBUtil类中,由于需要重用DButil类的getConnection()方法,设计人员将CustomerDAO作为DBUtil类的子类,如图:
随着客户数量的增多,系统决定升级为Oracle数据库,因此需要正价一个新的OracleDBUtil类来连接Oracle数据库,由于在初始设计方案中CustomerDAO和DButil之间是继承关系,因此在更换数据库连接方式时需要修改CutomerDAO类的源代码,将CustomerDAO作为OracleDBUtil的子类这将违背开闭原则,也可以直接修改DBUtil的源代码,但是也违背了开闭原则。
现在使用组合/聚合原则进行重构。
根据合成复用原则,在实现复用时应该多用关联,少用继承。在本实例中可以使用关联复用来取代继承复用,重构后的结构。
在图中,CustomerDAO和DBUtil之间的关系由继承关系变成关联关系,采用依赖注入的方式将DBUtil对象注入到CustomerDAO中,也可以使用构造注入,也可以使用设值注入。如果需要对DBUtil的功能进行扩展,可以通过其子类来实现,例如通过自来OracleDBUtil来连接Oracle数据库。由于CustomerDAO针对DBUtil编程,根据里氏替换原则,DBUtil子类的对象可以覆盖DButil对象,只需要在CustomerDAO中注入子类对象,就可以使用子类所扩展的方法。例如在CustomerDAO中注入OracleDBUtil对象,及可实现Oracle数据库连接,原有代码无需进行修改,而且可以很灵活的增加新的数据库连接方式,符合开闭原则。
后记
对于设计模式七大原则的学习,小编的博客只是对小编自己阶段学习的一个总结,其中有不合理的地方环境大家指出一起交流,并且小编学习的内容主要源自于书籍。
参考数据《Java设计模式》、《图解设计模式》。