首页/开发/代码质量与评审速查/组合优于继承(Favor Composition over Inheritance)
设计原则与模式62 条

组合优于继承(Favor Composition over Inheritance)

优先用组合与委托获得复用:继承把子类绑死在父类实现与接口上,组合可以运行期替换、只暴露需要的能力,是 GoF 在《设计模式》中给出的核心取向。

组合优于继承是什么

优先用组合与委托获得复用:继承把子类绑死在父类的实现与接口上,父类改动可能波及所有子类;组合可以运行期替换实现,只暴露需要的能力。这条取向来自《设计模式》的总结,判断标准是语义——稳定的是一种关系用继承,以复用为目的、行为需要插拔时用组合。

基本信息

继承适合是一种的语义稳定,契约可被完整继承
组合适合行为需要插拔,以复用为主要目的
实现手段委托加接口,必要时引入策略模式
判断信号子类覆写了父类的大部分方法
代价对比继承编译期绑定,组合可运行期替换

典型使用场景

  • 需要按环境切换压缩、加密或存储实现
  • 子类只用到父类的一小部分能力
  • 继承层次超过两层并开始出现空实现
  • 复用逻辑来自多个不相关的来源
  • 要给已有类增加能力,但不想改动它所在的继承链

对照示例

对照示例
// 继承:压缩实现被绑死在父类上,换实现要改类型
// class StrongExporter extends Compressor { ... }

// 组合:只暴露需要的能力,实现可替换
class Exporter {
    private final Compressor compressor;

    Exporter(Compressor compressor) { this.compressor = compressor; }

    byte[] export(Data data) { return compressor.compress(data); }
}

继承与组合的选择

维度继承组合
关系语义是一种有一个
绑定时机编译期确定运行期可替换
暴露程度父类接口全部继承只暴露需要的方法
变化影响父类改动波及子类实现替换互不影响
判断信号很实用:如果子类覆写了父类大多数方法,或层次里出现大量空实现,说明继承被当作复用手段而不是关系表达,应该改成组合。
用组合却把被委托对象的全部接口原样暴露,等于换了一层皮的继承:调用方仍与具体实现强耦合,替换实现时照样要改所有调用点。

易错点

  • 用组合但暴露全部被委托接口:等于换皮继承,替换成本没有降低
  • 需要行为可替换的封闭类型集合硬套组合:引入不必要的间接,调用方还得自己选实现
  • 为复用而继承:父类变化波及所有子类,回归范围失控
  • 层次过深:改动一层要在脑中同时装下所有祖先的行为

数据来源与参考

  • 《设计模式:可复用面向对象软件的基础》(GoF)· 设计原则建议
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌