应优先用组合替代继承,将可变逻辑剥离为显式策略对象;隔离protected字段、隐含调用链和构造期上下文;通过窄接口委托实现复用;常量与结构体应独立管理,避免继承传递。

直接用组合替代继承,能天然避开父类 protected 字段暴露、子类误读内部状态、版本升级引发计数错或校验绕过等风险。关键不是“不用继承”,而是把可变逻辑从基类中剥离,转为显式持有的策略对象。
识别并隔离易暴露的变量与行为
以下几类内容一旦放在父类里,极易被子类意外依赖或破坏:
-
protected 字段:如
errors列表、retryCount、lastModified等,子类直接读写等于共享内存,一改全崩 -
虚方法中隐含的调用链:比如
save()内部调用了validate()和log(),子类重写validate()却忘了log()依赖它 - 构造时隐式初始化的上下文:如父类构造器中自动注册监听器、加载配置,子类无法跳过或定制
用接口+委托实现安全复用
不继承行为,而是定义清晰接口,主类持有一个实例,所有调用都走委托:
- 把校验逻辑抽成
OrderValidator接口,OrderService持有其实现,而非继承某个ValidatedService - 把支付处理封装为
PaymentProcessor,Order类只包含一个private final PaymentProcessor processor字段 - 测试时直接注入
MockProcessor,生产用AlipayProcessor,切换零成本,不碰任何父类字段
避免“胖包装器”,控制组合粒度
组合不是简单套个字段,重点在接口是否正交、委托是否干净:
- 不要让
Order同时暴露processor.validate()、processor.format()、processor.retry()—— 这说明接口太宽,应按职责拆分 - 推荐按场景定义窄接口:
PaymentValidator、PaymentExecutor、PaymentRefunder,每个只做一件事 - 主类对外只提供高阶语义方法,如
submitPayment(),内部协调多个策略,不泄露底层细节
常量与结构体单独管理,不靠继承传递
像错误码、元数据结构、状态枚举这类静态内容,不该塞进继承链里“复用”:
- 定义
ErrorCode枚举或ApiResult<T>泛型类,所有业务类直接引用,不继承 - 把
DataMeta(含version、traceId、tenantId)做成不可变值对象,由各服务按需构造并传入,不靠父类字段承载 - 这样即使某天要给
tenantId加加密逻辑,或把traceId改成雪花ID,只改一个地方,无继承污染

















