“组合优于继承”是应对臃肿继承树的关键策略,通过将行为抽为独立组件、用策略模式解耦变化点、保留必要共用逻辑并封装为工具类,实现低耦合、高内聚的设计。

在 Java 中,当继承树变得臃肿、职责不清或子类大量复用父类中不相关的功能时,“组合优于继承”就成为关键重构策略——它不是拒绝继承,而是用更灵活、低耦合的方式替代深度/宽泛的继承关系。
识别继承树中的冗余信号
先判断是否真该重构:如果出现以下情况,说明继承正在损害可维护性:
- 子类只用父类 20% 的方法,却被迫继承全部字段和逻辑
- 多个子类重复覆盖同一个父类方法(如
process()),且实现差异大 - 为了支持新业务,不断往父类添加条件分支(
if (type == X)) - 单元测试必须 mock 整个父类链才能验证一个子类行为
用组合替换“伪 IS-A”关系
继承应严格用于表达“是某种类型”(如 Dog 是 Animal),而非“能做某事”。把行为抽成独立组件,让类通过字段持有它们:
比如原继承结构:
立即学习“Java免费学习笔记(深入)”;
class ReportGenerator extends PDFExporter implements EmailSender { ... }
重构为:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
class ReportGenerator {
private final PDFExporter pdfExporter;
private final EmailSender emailSender;
ReportGenerator(PDFExporter pdf, EmailSender email) {
this.pdfExporter = pdf;
this.emailSender = email;
}
void generateAndSend() {
byte[] pdf = pdfExporter.export(data);
emailSender.send("Report", pdf);
}
}
这样 ReportGenerator 不再是“一种导出器”,而是“使用导出器和发送器的业务协调者”——语义清晰,易于替换实现(如换用 CloudStorageUploader)。
用策略模式解耦变化点
当继承树中存在按类型分支的行为(如不同支付方式的 pay()),不要靠子类重写,而用策略接口 + 组合:
interface PaymentStrategy {
void pay(Order order);
}
class CreditCardPayment implements PaymentStrategy { ... }
class AlipayPayment implements PaymentStrategy { ... }
class OrderProcessor {
private final PaymentStrategy strategy;
OrderProcessor(PaymentStrategy strategy) {
this.strategy = strategy;
}
void execute(Order order) {
strategy.pay(order); // 运行时决定行为
}
}
新增支付方式只需加新策略类,无需修改 OrderProcessor 或创建新继承分支。
保留必要继承,封装共用逻辑到工具类或委托类
不是所有父类都要消灭。若确实存在通用状态和行为(如 ID、创建时间、基础校验),可:
- 提取为
BaseEntity类,但仅含真正共用的字段和 final 方法 - 将可变逻辑(如序列化、审计)封装进
JsonSerializer、AuditLogger等组合对象 - 用构造器或 Builder 注入这些组件,避免子类被动继承无关能力
例如:User 和 Product 都需要生成唯一 ID,但不需要共享数据库操作——那就组合一个 IdGenerator,而不是让它们同继承 DatabaseEntity。
重构不是一步到位,而是从最僵硬、复用率最低的子类开始,逐步用组合替代继承,同时保持接口兼容。核心是让每个类只承担单一职责,并通过明确的“拥有”关系协作,而非模糊的“是”关系绑定。

















