4.7 合成复用原则

一. 合成复用原则的定义

合成复用原则(Composite Reuse Principle,CRP)又叫组合/聚合复用原则(Composition/Aggregate Reuse Principle,CARP)。

它要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

如果要使用继承关系,则必须严格遵循里式替换原则。合成复用原则同里式替换原则相辅相成,两者都是开闭原则的具体实现规范;

1. 核心思想

1)找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起;

2)针对接口编程,而不是针对实现编程;

3)为了交互对象之间的松耦合设计而努力;

2. 重要性

通常类的复用分为 继承复用 和 合成复用 两种,继承复用虽然有简单和易实现的优点,但它也存在以下缺点:

1. 继承复用破坏了类的封装性。它将父类的实现细节暴露给子类,父类对子类是透明的,故这种复用又称为“白箱”复用。

2. 子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。

3. 它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。

采用 组合或聚合复用 时,可将已有对象纳入新对象中,使之成为新对象的一部分,新对象可调用已有对象的功能,它有以下优点:

1. 它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱”复用。

2. 新旧类之间的耦合度低。这种复用所需的依赖较少,新对象存取成分对象的唯一方法是通过成分对象的接口。

3. 复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。

二. 实现方法

合成复用原则是通过将已有的对象纳入新对象中,作为新对象的成员对象来实现的,新对象可以调用已有对象的功能从而达到复用。

三. 实例

下面以汽车分类管理程序为例来介绍合成复用原则的应用。

汽车分类管理程序

1. 分析:汽车按“动力源”划分可分为汽油汽车、电动汽车等;按“颜色”划分可分为白色汽车、黑色汽车和红色汽车等。如果同时考虑这两种分类,其组合就很多。图 1 所示是用继承关系实现的汽车分类的类图。

从图 1 可以看出用继承关系实现会产生很多子类,而且增加新的“动力源”或者增加新的“颜色”都要修改源代码,这违背了开闭原则,

显然不可取。但如果改用组合关系实现就能很好地解决以上问题,其类图如图 2 所示。

2.代码示例

fun main() {
    val gasolineCar = GasolineCar(Color(Color.RED))
    gasolineCar.display()
    val electricCar = ElectricCar(Color(Color.WHITE))
    electricCar.display()
}

class Color (var colorDes:String){
    companion object{
        val BLACK = "BLACK"

        val WHITE = "WHITE"

        val RED ="RED"
    }
}

abstract class Car(protected open var color: Color=Color(Color.WHITE)) {
    abstract fun move()
    abstract fun display()
}

class GasolineCar(override var color: Color):Car(color){
    override fun move() {}
    override fun display() {
        println("this is a ${color.colorDes} gasolineCar")
    }
}

class ElectricCar(override var color: Color):Car(color){
    override fun move() {}
    override fun display() {
        println("this is a ${color.colorDes} electricCar")
    }
}

程序的运行结果如下:

this is a RED gasolineCar
this is a WHITE electricCar

四. 七大架构设计原则

各种原则要求的侧重点不同,下面我们分别用一句话归纳总结软件设计模式的七大原则,如下表所示。

设计原则

一句话归纳

目的

开闭原则(OCP)

对扩展开放,对修改关闭

降低维护带来的新风险

依赖倒置原则(DIP)

高层不应该依赖底层,要面向接口编程

更利于代码结构的升级扩展

单一职能原则(SRP)

一个类只干一件事,实现类要单一(封装)

便于理解,提高代码的可读性

接口隔离原则(ISP)

一个接口只干一件事,接口要精简单一(接口粒度小)

功能解耦,高内聚、低耦合

迪米特法则(LoD)

不该知道的不要知道,一个类应保持对其他对象最少的了解

只和直接朋友交流,不和陌生人说话

里氏替换原则(LSP)

不要破坏继承体系,子类重写方法功能不应影响父类方法的含义

防止继承泛滥

合成复用原则(CRP)

尽量使用组合或聚合关系实现代码复用,少使用继承

降低代码耦合 将is 转换成 has

实际上,这些原则的目的只有一个:降低对象之间的耦合,增加程序的可复用性、可扩展性和可维护性。

记忆口诀:访问加限制,函数要节俭,依赖不允许,动态加接口,父类要抽象,扩展不更改。

在程序设计时,我们应该将程序功能最小化,每个类只干一件事。若有类似功能基础之上添加新功能,则要合理使用继承。对于多方法的调用,要会运用接口,同时合理设置接口功能与数量。最后类与类之间做到低耦合高内聚。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值