合成聚合复用原则_动力节点Java学院整理

合成聚合复用原则

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

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

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

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

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

扩展

对于继承的深入理解,大家可以参考《软件架构设计》一书作者温昱先生的文章——《见山只是山见水只是水——提升对继承的认识》。

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

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

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

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数据库连接,原有代码无须进行修改,而且还可以很灵活地增加新的数据库连接方式。

(0)

相关推荐

  • ThreadLocal使用案例_动力节点Java学院整理

    用户提出一个需求:当修改产品价格的时候,需要记录操作日志,什么时候做了什么事情. 想必这个案例,只要是做过应用系统的小伙伴们,都应该遇到过吧?无外乎数据库里就两张表:product 与 log,用两条 SQL 语句应该可以解决问题: update product set price = ? where id = ? insert into log (created, description) values (?, ?) But!要确保这两条 SQL 语句必须在同一个事务里进行提交,否则有可能 u

  • 工厂方法模式_动力节点Java学院整理

    定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类. 类型:创建类模式 类图: 工厂方法模式代码 interface IProduct { public void productMethod(); } class Product implements IProduct { public void productMethod() { System.out.println("产品"); } } interface IFactory { publ

  • JNDI在JavaEE中的角色_动力节点Java学院整理

    虽然 J2EE 平台提高了普通企业开发人员的生活水平,但是这种提高是以不得不学习许多规范和技术为代价的,这些规范和技术则是 J2EE 为了成为无所不包的分布式计算平台而整合进来的.Dolly Developer 是众多开发人员中的一员,她已经发现了一个特性,该特性有助于缓解随企业级应用程序部署而带来的负担,这个特性就是 JNDI,即 Java 命名与目录接口(Java Naming and Directory Interface).让我们来看看 Dolly 在没有 JNDI 的时候是怎么做的,以

  • JNDI简介_动力节点Java学院整理

    一.JNDI是什么? JNDI--Java 命名和目录接口(Java Naming and Directory Interface),是一组在Java应用中访问命名和目录服务的API. 二.JNDI好处 解耦:通过注册.查找JNDI服务,可以直接使用服务,而无需关心服务提供者,这样程序不至于与访问的资源耦合! JNDI优点  包含了大量的命名和目录服务,使用通用接口来访问不同种类的服务: 可以同时连接到多个命名或目录服务上: 建立起逻辑关联,允许把名称同Java对象或资源关联起来,而不必知道对象

  • ThreadLocal简介_动力节点Java学院整理

    ThreadLocal,直译为"线程本地"或"本地线程",如果你真的这么认为,那就错了!其实,它就是一个容器,用于存放线程的局部变量,我认为应该叫做 ThreadLocalVariable(线程局部变量)才对,真不理解为什么当初 Sun 公司的工程师这样命名. 早在 JDK 1.2 的时代,java.lang.ThreadLocal 就诞生了,它是为了解决多线程并发问题而设计的,只不过设计得有些难用,所以至 今没有得到广泛使用.其实它还是挺有用的,不相信的话,我们一

  • ocp开闭原则_动力节点Java学院整理

    开闭原则(Open Closed Principle)是Java世界里最基础的设计原则,它指导我们如何建立一个稳定的.灵活的系统. 定义: 一个软件实体如类.模块和函数应该对扩展开放,对修改关闭. Softeware entities like classes,modules and functions should be open for extension but closed for modifications. 开闭原则的含义是说一个软件实体应该通过扩展来实现变化,而不是通过修改已有代码

  • 合成聚合复用原则_动力节点Java学院整理

    合成聚合复用原则 合成复用原则又称为组合/聚合复用原则(Composition/Aggregate Reuse Principle, CARP),其定义如下: 合成复用原则(Composite Reuse Principle, CRP):尽量使用对象组合,而不是继承来达到复用的目的. 合成复用原则就是在一个新的对象里通过关联关系(包括组合关系和聚合关系)来使用一些已有的对象,使之成为新对象的一部分:新对象通过委派调用已有对象的方法达到复用功能的目的.简言之:复用时要尽量使用组合/聚合关系(关联关

  • 里氏代换原则_动力节点Java学院整理

    聊一聊里氏代换原则. 定义1:如果对每一个类型为 T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型. 定义2:所有引用基类的地方必须能透明地使用其子类的对象. 问题由来:有一功能P1,由类A完成.现需要将功能P1进行扩展,扩展后的功能为P,其中P由原有功能P1与新功能P2组成.新功能P由类A的子类B来完成,则子类B在完成新功能P2的同时,有可能会导致原有功能

  • Java依赖倒转原则_动力节点Java学院整理

    定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象:抽象不应该依赖细节:细节应该依赖抽象. 问题由来:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成.这种场景下,类A一般是高层模块,负责复杂的业务逻辑:类B和类C是低层模块,负责基本的原子操作:假如修改类A,会给程序带来不必要的风险. 解决方案:将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率. 依赖倒置原则基于这样一个事实:相对于细节的多变性,

  • 高内聚低耦合原则_动力节点Java学院整理

    一.什么是耦合度 软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准.划分摸块的一个准则就是高内聚低耦合. 耦合度(Coupling)是对模块间关联程度的度量.耦合的强弱取决与模块间接口的复杂性.调用模块的方式以及通过界面传送数据的多少. 模块间的耦合度是指模块之间的依赖关系,包括控制关系.调用关系.数据传递关系.模块间联系越多,其耦合性越强,同时表明其独立性越差.降低模块间的耦合度能减少模块间的影响,防止对某一模块修改所引起的"牵一发动全身"的水波效应,保证系统设计顺利进行.

  • 接口隔离原则_动力节点Java学院整理

    定义:客户端不应该依赖它不需要的接口:一个类对另一个类的依赖应该建立在最小的接口上. 问题由来:类A通过接口I依赖类B,类C通过接口I依赖类D,如果接口I对于类A和类B来说不是最小接口,则类B和类D必须去实现他们不需要的方法. 解决方案:将臃肿的接口I拆分为独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系.也就是采用接口隔离原则. 举例来说明接口隔离原则: (图1  未遵循接口隔离原则的设计) 这个图的意思是:类A依赖接口I中的方法1.方法2.方法3,类B是对类A依赖的实现.类C依赖接

  • 单一职责原则_动力节点Java学院整理

    定义:不要存在多于一个导致类变更的原因.通俗的说,即一个类只负责一项职责. 问题由来:类T负责两个不同的职责:职责P1,职责P2.当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障. 解决方案:遵循单一职责原则.分别建立两个类T1.T2,使T1完成职责P1功能,T2完成职责P2功能.这样,当修改类T1时,不会使职责P2发生故障风险:同理,当修改T2时,也不会使职责P1发生故障风险. 说到单一职责原则,很多人都会不屑一顾.因为它太简单了.稍有经验的程序员即使

  • 开放封闭原则_动力节点Java学院整理

    定义:软件实体(类.模块.函数等)应该是可以扩展的,但是不可修改.对于扩展是开放的,对于更改是封闭的.关键是抽象,将一个功能的通用部分和实现细节部分清晰的分离开来. 这里要求我们写代码要有抽象的概念.什么是抽象?指由实体抽离出概念的思考过程.就是从众多的对象中抽离出共同的本质的特征.在写代码的过程中,需要抽象类的地方,只需要抓住这个类的本质功能,不要老想着它在这个工程中的具体功能. 我们继续看开放封闭原则,这个原则要求,将一个功能的共用部分和实现部分清晰地分离开来.因为在最初搭建架构的时候你不能

  • Nginx简介_动力节点Java学院整理

    1.什么是Nginx Nginx来自俄罗斯的Igor Sysoev在为Rambler Media(http://www.rambler.ru/)工作期间,使用C语言开发了Nginx.Nginx作为Web服务器,一直为俄罗斯著名的门户网站Rambler Media提供着出色.稳定的服务. Igor Sysoev将Nginx的代码开源,并且赋予其最自由的2-clause BSD-like license许可证.由于Nginx使用基于事件驱动的架构能够并发处理百万级别的TCP连接,高度模块化的设计和自

  • web压力测试工具_动力节点Java 学院整理

    0. Grinder –  Grinder是一个开源的JVM负载测试框架,它通过很多负载注射器来为分布式测试提供了便利. 支持用于执行测试脚本的Jython脚本引擎HTTP测试可通过HTTP代理进行管理.根据项目网站的说法,Grinder的 主要目标用户是"理解他们所测代码的人--Grinder不仅仅是带有一组相关响应时间的'黑盒'测试.由于测试过程可以进行编码--而不是简单地脚本 化,所以程序员能测试应用中内部的各个层次,而不仅仅是通过用户界面测试响应时间. 1. Pylot -Pylot 是

随机推荐