面向对象设计原则之合成复用原则

合成复用原则又称为组合/聚合复用原则(Composition/Aggregate Reuse Principle, CARP),其定义以下:数据库

合成复用原则(Composite Reuse Principle, CRP):尽可能使用对象组合,而不是继承来达到复用的目的。编程

      合成复用原则就是在一个新的对象里经过关联关系(包括组合关系和聚合关系)来使用一些已有的对象,使之成为新对象的一部分;新对象经过委派调用已有对象的方法达到复用功能的目的。简言之:复用时要尽可能使用组合/聚合关系(关联关系),少用继承架构

      在面向对象设计中,能够经过两种方法在不一样的环境中复用已有的设计和实现,即经过组合/聚合关系或经过继承,但首先应该考虑使用组合/聚合,组合/聚合可使系统更加灵活,下降类与类之间的耦合度,一个类的变化对其余类形成的影响相对较少;其次才考虑继承,在使用继承时,须要严格遵循里氏代换原则,有效使用继承会有助于对问题的理解,下降复杂度,而滥用继承反而会增长系统构建和维护的难度以及系统的复杂度,所以须要慎重使用继承复用。spa

      经过继承来进行复用的主要问题在于继承复用会破坏系统的封装性,由于继承会将基类的实现细节暴露给子类,因为基类的内部细节一般对子类来讲是可见的,因此这种复用又称“白箱”复用,若是基类发生改变,那么子类的实现也不得不发生改变;从基类继承而来的实现是静态的,不可能在运行时发生改变,没有足够的灵活性;并且继承只能在有限的环境中使用(如类没有声明为不能被继承)。架构设计

 

扩展设计

对于继承的深刻理解,你们能够参考《软件架构设计》一书做者温昱先生的文章——《见山只是山见水只是水——提高对继承的认识》。对象

 

      因为组合或聚合关系能够将已有的对象(也可称为成员对象)归入到新对象中,使之成为新对象的一部分,所以新对象能够调用已有对象的功能,这样作可使得成员对象的内部实现细节对于新对象不可见,因此这种复用又称为“黑箱”复用,相对继承关系而言,其耦合度相对较低,成员对象的变化对新对象的影响不大,能够在新对象中根据实际须要有选择性地调用成员对象的操做;合成复用能够在运行时动态进行,新对象能够动态地引用与成员对象类型相同的其余对象。继承

      通常而言,若是两个类之间是“Has-A”的关系应使用组合或聚合,若是是“Is-A”关系可以使用继承。"Is-A"是严格的分类学意义上的定义,意思是一个类是另外一个类的"一种";而"Has-A"则不一样,它表示某一个角色具备某一项责任。ip

      下面经过一个简单实例来加深对合成复用原则的理解:ci

      Sunny软件公司开发人员在初期的CRM系统设计中,考虑到客户数量很少,系统采用MySQL做为数据库,与数据库操做有关的类如CustomerDAO类等都须要链接数据库,链接数据库的方法getConnection()封装在DBUtil类中,因为须要重用DBUtil类的getConnection()方法,设计人员将CustomerDAO做为DBUtil类的子类,初始设计方案结构如图1所示:

图1  初始设计方案结构图

      随着客户数量的增长,系统决定升级为Oracle数据库,所以须要增长一个新的OracleDBUtil类来链接Oracle数据库,因为在初始设计方案中CustomerDAO和DBUtil之间是继承关系,所以在更换数据库链接方式时须要修改CustomerDAO类的源代码,将CustomerDAO做为OracleDBUtil的子类,这将违反开闭原则。【固然也能够修改DBUtil类的源代码,一样会违反开闭原则。】

      现使用合成复用原则对其进行重构。

      根据合成复用原则,咱们在实现复用时应该多用关联,少用继承。所以在本实例中咱们可使用关联复用来取代继承复用,重构后的结构如图2所示:

图2  重构后的结构图

      在图2中,CustomerDAO和DBUtil之间的关系由继承关系变为关联关系,采用依赖注入的方式将DBUtil对象注入到CustomerDAO中,可使用构造注入,也可使用Setter注入。若是须要对DBUtil的功能进行扩展,能够经过其子类来实现,如经过子类OracleDBUtil来链接Oracle数据库。因为CustomerDAO针对DBUtil编程,根据里氏代换原则,DBUtil子类的对象能够覆盖DBUtil对象,只需在CustomerDAO中注入子类对象便可使用子类所扩展的方法。例如在CustomerDAO中注入OracleDBUtil对象,便可实现Oracle数据库链接,原有代码无须进行修改,并且还能够很灵活地增长新的数据库链接方式。

相关文章
相关标签/搜索