重构继承为组合的核心是将“子类is-a父类”的强依赖改为“新类has-a成员对象”的松耦合,需重新建模关系、转移职责、封装细节;识别误用场景如仅为复用而继承、滥用protected成员、浅重写、过深继承链;通过提取能力为独立类、声明成员引用、委托调用、按需暴露接口四步完成替换;构造时传入依赖以利测试;多态需求改用策略模式实现。

重构继承为组合,核心是把“子类 is-a 父类”的强依赖,改成“新类 has-a 成员对象”的松耦合。这不是简单替换语法,而是重新建模关系、转移职责、封装细节。
识别可被替代的继承场景
先判断当前继承是否真正符合“Is-A”语义。常见误用包括:
- 只是为了复用方法,而子类和父类没有天然分类关系(比如
Manager extends Employee合理,但Stack extends ArrayList就违背语义) - 父类含大量 protected 方法或字段,子类频繁访问实现细节
- 子类只重写少数方法,其余全部直接调用父类,且不涉及多态行为
- 继承链过深(三层以上),修改父类易引发连锁问题
用组合替换继承的四步操作
以一个典型误用为例:ReportGenerator extends DatabaseConnection(报表生成器“是一个”数据库连接?显然不合理):
-
提取共用能力为独立类:将
DatabaseConnection中的查询、连接管理逻辑封装成DbClient类,去掉继承结构 -
在新类中声明成员引用:在
ReportGenerator中添加private final DbClient db; -
委托调用代替继承调用:原来
super.executeQuery(...)改为db.executeQuery(...) -
按需暴露接口,不暴露实现:
ReportGenerator只提供generateMonthlyReport(),内部调用db,外部无需知道它用的是 MySQL 还是 PostgreSQL
处理构造与初始化逻辑
继承中常依赖 super(...) 初始化父类状态,改用组合后需主动管理依赖生命周期:
- 构造器中传入依赖(推荐):
public ReportGenerator(DbClient db) { this.db = Objects.requireNonNull(db); } - 避免在类内直接
new DbClient(),便于单元测试和替换实现 - 若原继承有复杂初始化顺序,可将初始化逻辑抽为
init()方法,由组合对象自行控制
保留必要多态时的替代方案
如果原继承是为了实现多态(如不同 PaymentProcessor 子类),不要删掉抽象,而是用组合+策略模式:
- 定义
interface PaymentStrategy,各实现类(AlipayStrategy,WechatStrategy)独立实现 -
PaymentProcessor持有private PaymentStrategy strategy; - 运行时动态切换策略:
processor.setStrategy(new WechatStrategy()); - 这样既解耦,又支持扩展,还避免了继承树膨胀
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











