首页/开发/代码质量与评审速查/手法:Replace Superclass with Delegate(以委托替换继承)
重构手法62 条

手法:Replace Superclass with Delegate(以委托替换继承)

子类关系只为复用父类实现而建立时,改为持有父类实例并转发需要的能力:类型语义不再被伪造,继承带来的多余接口消失;组合优先的具体应用。

是什么

以委托替换继承(Replace Superclass with Delegate)处理的是只为复用实现而建立的继承关系:让子类改为持有父类的一个实例,把真正需要的操作转发过去。这样类型语义不再被伪造,继承带来的多余接口也随之消失,替换被委托对象时也更自由。它是组合优先这一原则在重构层面的具体落法。

信号与做法

信号继承只是为了复用代码,父类的部分方法对子类并不适用
做法子类持有父类实例,按白名单定向转发需要的能力
收益公开接口收窄,实现替换更自由,类型语义与用法一致
对照若类型语义成立则保留继承,例如确实存在是一种的关系

典型使用场景

  • 容器类继承集合类:只需栈或队列语义,却被动继承了整套随机访问接口
  • 工具基类被当成代码复用手段:各子类只用到基类一两个方法
  • 框架基类携带大量可选钩子:用组合后只实现真正必要的能力
  • 需要替换底层实现:委托对象的类型可以独立变化,继承层级做不到
  • 测试替身难写:继承层级深导致构造复杂,改为委托后可以只替换被委托方

改造示例

改造示例
// 改造前:Stack 继承 Vector,只为了复用底层存储
class Stack extends Vector { }

// 改造后:持有委托对象,只转发栈需要的操作
class Stack {
    private final Deque<Object> elements = new ArrayDeque<>();
    void push(Object o) { elements.push(o); }
    Object pop() { return elements.pop(); }
    Object peek() { return elements.peek(); }
}

改造步骤与检查

步骤动作检查点
一在子类中加入父类字段并初始化构造参数变化是否影响所有调用点
二把子类方法改为转发调用只转发实际需要的操作,不照搬全部
三解除继承关系类型检查与多态调用处是否仍成立
四替换外部引用外部是否按父类类型引用了子类实例
五补齐隐含契约相等性、哈希与克隆语义是否被改变
转发采用白名单方式:只暴露确实需要的能力,而不是机械地把父类所有公开方法都转发一遍。后者会让新类型的接口宽度与父类相同,暴露面问题并未解决。
关闭继承关系时留意父类的隐含契约。原先继承得到的相等性、哈希值、字符串表示与克隆行为在委托形态下不再自动具备,若调用方曾依赖其中任何一项,需要显式实现。

落地检查清单

  • 确认原继承关系不是真实的类型语义关系
  • 转发集合经过筛选,接口宽度明显收窄
  • 外部引用点全部改为新类型,编译通过
  • 补上必要的行为契约与相关测试
  • 类文档说明被委托对象的替换方式

易错点

  • 转发时照搬父类全部方法:新类型接口过宽,等于换了写法保留原问题,白名单式转发才有效
  • 遗漏父类隐含契约:相等性、哈希或克隆行为改变,集合与缓存中出现意外结果
  • 外部仍按父类类型引用子类:解除继承后编译失败,需要一次性梳理全部引用点
  • 为消除继承而机械改造:语义上成立的继承关系被拆散,多出无价值的转发代码

数据来源与参考

  • 《重构》第 2 版 · 第 12 章(Replace Superclass with Delegate)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌