继承易破坏封装性,因子类依赖父类内部实现;应优先用组合替代继承,控制继承深度(Java/C#≤2层),谨慎开放虚方法,多用接口定义能力而非类继承。

继承确实容易破坏封装性,核心问题在于子类过度依赖父类的内部实现细节——一旦父类改动,所有子类都可能意外失效。这不是继承本身有错,而是使用方式需要更谨慎。
优先用组合替代继承
把“is-a”关系换成“has-a”,能天然规避基类变更带来的连锁反应。
- 比如一个
PaymentProcessor类,不要让它继承BaseValidator,而是持有一个Validator接口实例 - 这样替换验证策略时,只需注入新实现,无需修改
PaymentProcessor结构 - 测试也更简单:直接传入Mock对象,不依赖基类构造逻辑
限制继承链深度
超过三层的继承树会显著放大脆弱基类风险,阅读和调试成本陡增。
- Java/C#中建议控制在2层以内(如
Animal → Mammal → Dog已到极限) - C++若必须多层,对中间层使用
final关键字封禁进一步派生 - 定期重构:把深层继承中稳定的部分提取为独立组件,改用组合接入
虚方法要明确契约,而非开放一切
不是所有方法都该是virtual或protected——无约束的可重写性等于主动放弃封装。
- 只将真正需要定制的行为设为虚方法,并在文档中写清调用约定(是否必须调用
base、前置条件、副作用限制) - 用模板方法模式固化主流程,把可变点收缩为少数几个受控钩子
- 避免在基类中暴露字段,一律通过
protected virtual属性或方法间接访问
用接口定义能力,而非用类定义身份
比起继承具象类,用接口描述“能做什么”,让实现彼此解耦。
- 例如定义
IResizable、IDraggable,而不是让Window继承UIElement再继承Component - 一个类可以同时实现多个接口,灵活组合行为,不引入不必要的继承依赖
- 接口变更影响范围可控,而基类修改可能波及几十个子类

















