控制继承深度在两层以内,用组合+接口替代中间抽象层,基类只保留不可变契约,定期识别扇出过高、空方法重写等退化信号并重构。

控制继承深度在两层以内是最直接有效的做法,不是限制能力,而是守住可维护性的底线。
把继承层级硬性卡在两层
比如 Animal → Dog 是合理的一层;Dog → ServiceDog 就已到上限。再往下延伸(如 ServiceDog → CertifiedServiceDog),大概率不是类型本质变化,而是配置、策略或角色差异——这些该用参数、枚举或策略对象表达,不该靠新增子类。
团队可在代码规范中明确:所有具体实现类标记为 final,禁止被继承;用 SonarQube 或 IDEA 的 “Inheritance depth” 检查,在 CI 阶段拦截三层及以上结构。
用组合 + 接口替代中间抽象层
当某层类(如 CachedService、ValidatedService)只是给多个子类“打补丁”,它实际承担的是能力装配职责,不是语义上的“是一种”。这类中间层是复杂度温床。
- 把 LoggingService 改为 Loggable 接口 + DefaultLogger 实现,由业务类通过字段持有
- 将 ValidatedService 抽成 ValidationRule 策略对象,构造时传入,而非继承固定逻辑
- 每个业务类只依赖所需能力,调用链清晰,单元测试只需 mock 单个组件
基类只保留不可变契约
脆弱的根源常是父类悄悄塞进本该由子类决定的行为:硬编码超时、默认日志级别、特定异常包装等。这些细节一旦写死,子类就只能绕着走或强行覆盖。
- 基类方法尽量声明为 final,仅留少数 hook(如 protected abstract doProcess())供定制核心逻辑
- 所有可变参数必须通过构造函数或 Builder 注入,不设默认值字段
- 基类单元测试只验证流程契约(如 “before → doProcess → after 必然按序执行”),不测具体行为
定期识别退化信号并重构
大型项目中,继承结构会悄然异化。以下信号出现时,就是该动手重构的提示:
- 类图中某父类扇出过高(被十几个子类继承)
- 大量子类只重写空方法(public void onInit() { })
- 多个子类行为高度雷同,仅字段值不同
发现即拆:把共性状态抽为独立组件(如 Timestamped、Versioned),把能力定义为接口(如 Exportable、Searchable),让业务类按需组合。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











