应优先用组合替代继承,即“我拥有什么”而非“我是什么”,通过接口+小而专的组件实现能力复用,构造注入解耦依赖,支持运行时策略切换与明确生命周期管理。

直接用组合替代复杂继承,不是简单把 extends 换成字段引用,而是把“我是一个什么”重新理解为“我拥有什么”,让类通过持有能力组件来协作,而不是靠层级压榨复用。
识别继承滥用的典型信号
当继承结构开始拖慢开发节奏,往往已有明显征兆:
- 子类大量重写父类方法,却只调用其中一两个逻辑,其余代码形同虚设
- 某个子类完全用不到父类里的某块功能(比如“内部测试订单”继承了支付校验,但实际不走支付)
- 新增一种业务变体(如“支持离线重试的订单”)就得在继承链上硬加一层,可它和现有类之间并不存在真实的“是一种”关系
- 类名越来越长,出现
ScrollableResizableWindowWithBorder这类拼接式命名
把能力拆成接口+小而专的实现
别再把日志、校验、通知、重试这些通用行为塞进基类。它们不是类的身份,而是它“拥有的能力”:
- 定义
Logger接口,提供info()、error();实现ConsoleLogger、FileLogger - 定义
PaymentStrategy接口,只暴露charge()和refund();实现AlipayStrategy、WechatStrategy - 每个组件只做一件事,不掺杂无关逻辑,也不试图成为“全能工具类”
用构造注入代替 extends
原写法:class OrderService extends BaseService;新写法:class OrderService,内部持有一组接口引用:
- 依赖类型声明为接口(
Logger),而非具体实现(FileLogger) - 所有组件通过构造函数传入,主类不自行
new - 可选能力用
Optional或可空类型包装,避免强制初始化 - 测试时直接传
MockLogger,上线时换真实实现,主逻辑零改动
支持运行时嵌套与策略切换
继承锁死在编译期,组合把选择权交还给运行时:
- 一个订单处理器可同时持有
PaymentStrategy、Notifier、RetryPolicy - 下单时根据用户地区动态获取策略:
strategyFactory.getStrategy(country) - 需要同时发短信和邮件?用
CompositeNotifier聚合多个INotificationSender实现,自身不继承任何具体通知器 - 嵌套不宜过深(如 A→B→C→D),若出现长委托链,优先考虑合并相邻组件或引入事件总线解耦
明确生命周期与所有权边界
组合不是往类里堆对象,得说清楚谁创建、谁销毁、谁负责:
- 组合(Composition):生命周期一致,如订单包含订单项 → 在构造函数中创建
- 聚合(Aggregation):生命周期独立,如用户持有邮箱服务 → 由 DI 容器或工厂注入
- 避免循环引用:A 持有 B,B 又持有 A,会导致初始化失败或内存泄漏
- 数据库连接、HTTP 客户端等资源型对象,统一交由容器管理,业务类不自行 new











