避免脆弱基类问题,核心是用接口定义契约而非抽象类塞逻辑;基类若存在须满足不被外部依赖、public方法final、参数通过构造器注入;优先组合替代继承,限制继承深度不超过两层。

避免脆弱基类问题,核心是不让子类被迫依赖父类的实现细节。父类一旦修改,子类就可能意外失效——这不是继承本身有错,而是用法越界了。
用接口定义契约,而不是用抽象类塞逻辑
接口只声明“能做什么”,不规定“怎么做”。比如日志功能,定义 EventLogger 接口,只含 log(String event) 方法;不要写一个 AbstractLogger 类,里面预置 filePath、bufferSize 字段和默认写文件逻辑。后者会让所有子类被绑死在文件路径和缓冲策略上。
- 每个实现类(如 FileLogger、SlackLogger)完全独立,互不感知
- 业务类只持有接口引用,不 import 任何具体实现类
- 新增日志渠道时,只需新增实现,不改已有代码
基类只做技术支撑,拒绝业务渗透
如果真需要抽象基类(例如统一处理 HTTP 重试、超时、Header 注入),它必须满足三个硬约束:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 自身不被外部直接依赖——不能作为 service 层返回类型或方法参数
- 所有 public 方法标记为 final,只留极少数 protected 钩子(如 doExecute())供子类定制核心步骤
- 所有可变参数(超时毫秒数、序列化格式、重试次数)必须通过构造器或 Builder 注入,不设字段默认值
用组合替代中间层继承
当看到类似 CachedUserService、ValidatedOrderService 这样的类名,说明你正在用继承“打补丁”。这类中间层不是语义上的“是一种”,而是能力装配角色。
立即学习“Java免费学习笔记(深入)”;
- 把缓存能力抽成 CachePolicy 策略对象,构造时传入业务类
- 把校验逻辑封装为 ValidationRule,由业务类按需组合
- 业务类不再继承,而是持有一组接口引用,职责清晰,测试也只需 mock 单个组件
限制继承深度,禁用子类再继承
继承树超过两层(如 A → B → C)就会显著放大风险。第三层往往只是加了个配置项或小修饰,不构成新类型。
- 团队规范中明确:所有具体实现类必须声明为 final
- 用 SonarQube 或 IDEA 的 “Inheritance depth” 检查,在 CI 阶段拦截三层及以上继承
- 发现 ServiceDog → CertifiedServiceDog 这类命名,优先考虑用属性、枚举或策略代替新增子类

















