用组合代替继承是避免继承树过深最直接有效的方式,即把“我是谁的子类”改为“我用了什么能力”,通过抽接口、提字段、委托调用三步重构,并分阶段迁移遗留系统。

用组合代替继承,是避免继承树过深最直接有效的方式。不是删掉父类,而是把“我是谁的子类”变成“我用了什么能力”,从而切断隐式、脆弱的依赖链。
识别该重构的坏味道
别急着改代码,先看是否真有必要动:
- 继承链达到四层及以上,比如 A extends B extends C extends D
- 子类只重写一两个方法,其余逻辑全靠继承父类,实际复用率极低
- 类名越来越长、修饰词堆砌,如 RetryableTransactionalAsyncHttpClient——说明多个正交职责被强行塞进一条继承线
- 父类混杂日志、缓存、重试、序列化等不相关功能,子类被迫全盘接收
三步落地:抽接口 + 提字段 + 委托调用
以典型例子 OrderService extends BaseService extends NetworkClient 为例:
-
抽接口:把网络调用能力单独抽象为 NetworkLayer 接口,只保留
send(Request)、timeout()等核心契约 -
提字段:在 OrderService 中去掉
extends,新增private final NetworkLayer network; -
委托调用:原来写
super.send(req)的地方,改为network.send(req);构造器接收 NetworkLayer 实例,不 new 具体实现
注意:不要暴露 getNetwork() 这类方法,否则封装就失效了。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
保持类型统一靠接口,不是靠继承
下游依赖应面向 NetworkLayer 这样的接口编程,而不是某个具体父类。这样既能替换实现(比如从 HttpUrlConnection 换成 OkHttp),又不会牵连整个继承链。接口还能天然支持多实现、多组合,比单继承灵活得多。
遗留系统分阶段迁移
老项目不能一刀切:
- 第一步:给现有继承链顶层加接口,让新代码先依赖接口
- 第二步:逐步把子类中对父类 protected 方法的调用,替换成对组合对象的委托
- 第三步:确认所有测试通过后,再删除无用的继承关系和冗余父类
每一步都确保单元测试绿灯,行为不变是前提。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










