继承层次过深导致隐式脆弱依赖,应以组合替代:识别四层以上链、低重写率、命名臃肿、职责混杂等坏味道;通过抽接口、提字段、委托调用三步重构;保持类型统一靠接口实现而非继承;遗留系统需分三步渐进迁移,确保测试通过。

继承层次过深不是“多写了几个 extends”那么简单,而是让类之间形成隐式、脆弱的依赖链——改父类一行 protected 方法,下游七八个子类行为就悄悄变了。用组合替换,核心不是删父类,而是把“我继承了什么”变成“我持有并使用什么”,切断这种传导式耦合。
先识别该拆的继承坏味道
别一上来就重构,先看哪些继承真该动:
- 四层及以上继承链(比如
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() 这种透出内部对象的方法,否则封装就失效了。
立即学习“Java免费学习笔记(深入)”;
处理类型兼容和运行时切换
继承天然支持 List<Service> 统一持有,组合后照样能保持这种便利:
- 保留顶层业务接口(如
OrderProcessor),所有组合类都实现它,不靠继承关系来统一类型 - 按需拆小接口,比如
SyncOrderProcessor和AsyncOrderProcessor,避免大而全的接口污染 - 需要换策略时(如从 HTTP 切到 gRPC),只需传入新
NetworkLayer实现,主类代码零改动
让旧代码平稳过渡
不能一刀切,尤其面对已有大量子类的遗留系统:
- 第一步:让原抽象父类(如
BaseService)也实现新提取的NetworkLayer等接口,内部仍用老逻辑,但对外提供标准契约 - 第二步:用 IDE 的“替换继承为组合”重构功能,批量将子类中对父类方法的调用,转为通过持有的组合对象访问
- 第三步:逐步把子类里重写的钩子方法(如
beforeSend())移到独立策略类中,主类只负责编排
整个过程靠原有单元测试兜底——只要所有测试还绿,行为就没变,只是依赖方式更清晰、更可控。


















