面象对象设计原则之七:合成复用原则(Composition/Aggregate Reuse Principle, CARP)
合成复用原则又称为组合/聚合复用原则(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数据库连接,原有代码无须进行修改,而且还可以很灵活地增加新的数据库连接方式。
面象对象设计原则之七:合成复用原则(Composition/Aggregate Reuse Principle, CARP)的更多相关文章
- 设计模式 第一天 UML图,设计模式原则:开闭原则、依赖倒转原则、接口隔离原则、合成复用原则、迪米特法则,简单工厂模式
1 课程大纲 2 UML的概述 总结: UML unified model language 统一建模语言 一共有十种图: 类图 用例图 时序图 * 对象图 包图 组件图 部署图 协作图 状态图 (最 ...
- Java设计模式(4:里氏替换原则和合成复用原则详解
一.里氏替换原则 如果说实现开闭原则的关键步骤就是抽象化,那么基类(父类)和子类的继承关系就是抽象化的具体实现,所以里氏替换原则就是对实现抽象化的具体步骤的规范.即:子类可以扩展基类(父类)的功能,但 ...
- 合成/聚合复用原则(Composite/Aggregate Reuse Principle, CARP)
尽量使用对象组合,而不是继承来达到复用的目的 未完待续
- 融会贯通——最常用的“合成复用原则”技能点Get
复用一个类的时候,多使用对象的组合/聚合的关联关系,而不是继承. 之前提到的"依赖倒转原则",是以里氏代换原则为基础的实现开闭原则目标的手段,这一条路线涉及到的是类的继承(包括单继 ...
- DesignPattern系列__07合成复用原则
基本介绍 合成复用原则的核心,就是尽量去使用组合.聚合等方式,而不是使用继承. 核心思想 1.找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起. 2.针对接口编程,而不是 ...
- 设计模式课程 设计模式精讲 3-11 合成复用原则coding
1 课堂概念 1.0 继承关系的选择 1.1 起名 1.2 定义 1.3 组合聚合优缺点 1.4 继承优缺点 1.5 组合聚合区别 2 代码演练 2.1 反例 2.2 正例 3 疑问解答3.1 疑问解 ...
- 面象对象设计原则之二:开放封闭原则(Open-Closed Principle, OCP)
开闭原则是面向对象的可复用设计的第一块基石,它是最重要的面向对象设计原则.开闭原则由Bertrand Meyer于1988年提出,其定义如下: 开闭原则(Open-Closed Principle, ...
- 面象对象设计原则之五:依赖倒置原则(The Dependency Inversion Principle,DIP)
如果说开闭原则是面向对象设计的目标的话,那么依赖倒转原则就是面向对象设计的主要实现机制之一,它是系统抽象化的具体实现.依赖倒转原则是Robert C. Martin在1996年为“C++Reporte ...
- 面象对象设计原则之三:里氏替换原则(The Liskov Substitution Principle,LSP)
里氏代换原则由2008年图灵奖得主.美国第一位计算机科学女博士Barbara Liskov教授和卡内基·梅隆大学Jeannette Wing教授于1994年提出.其严格表述如下:如果对每一个类型为S的 ...
随机推荐
- 深入理解Java 注解原理
*注解的用途 注解(Annotation)是JDK1.5引入的新特性,包含在java.lang.annotation包中,它是附加在代码中的一些元信息,将一个类的外部信息与内部成员联系起来,在编 译. ...
- 贪心——D - Radar Installation
Assume the coasting is an infinite straight line. Land is in one side of coasting, sea in the other. ...
- 两段锁协议(Two-Phase Locking――2PL)
两段锁协议(Two-Phase Locking――2PL) 两段锁协议规定所有的事务应遵守的规则: ① 在对任何数据进行读.写操作之前,首先要申请并获得对该数据的封锁. ② 在释放一个封锁之后,事务不 ...
- 解决.NET Web API生成的Help无Controller说明&服务端接收不到请求
今天在用.NET Web API写一个接口的时候遇到一个问题.在Controller中新加了一个方法,客户端就不能请求接口了,当时建WEB API项目是用的VS默认设置,在服务端打断点一直没有进去,而 ...
- fastjson 返回json字符串,JSON.parse 报错
这是由于转义字符引起的如 : \ , fastjson 处理后是双反斜杠:\\ ,而 JSON.parse 解析时需要4个反斜杠 ,即 js解析json 反斜杠时,需要 4个 解成 1 个 解决方法: ...
- a,b为2个整型变量,在不引入第三个变量的前提下写一个算法实现 a与b的值互换
package com.Summer_0424.cn; /** * @author Summer * a,b为2个整型变量,在不引入第三个变量的前提下写一个算法实现 a与b的值互换? */ publi ...
- python内置的高效好用各种库
二分查找,import bisect 堆排序,import heapq 哈希算法,import hashlib 压缩,lzma 图形处理,PIL 处理xml文件,PyXML 多媒体操作,PyMedia ...
- 【thinkPHP框架】Failed opening required 'header.php' include_path='.;c:\php5\pear 终级解决方案
ThinkPHP框架中的某一个php页面,想要去引入其他php页面,结果反复报错,无解. 各种百度,有说是文件权限不够的,也有说配置不对的,反正一个都没有解决这个问题,依然无解. 最终,找到了办法,似 ...
- iOS开发简记(7):网络请求模块
主流的APP都少不了跟服务器交互,网络请求是少不了的事情. 开源的网络请求库,有很多,比如:AFNetworking.YTKNetwork.PPNetworkHelper.ASIHttpRequest ...
- jqGrid之treeGrid及行拖拽
单纯的做个小记录 今天做功能用到了jqGrid里面的treeGrid,遇到几个问题,这里做下记录 treeGrid 树表格的应用在官网给出了很直白的例子: 1.http://blog.mn886.ne ...
