组合优于继承,通过持有对象复用行为而非绑定父类实现;接口定义契约,运行时注入具体实现;拆分职责为单一组件,明确生命周期管理,实现松耦合与高内聚。

继承导致紧耦合,本质是子类被绑死在父类的实现细节上——字段怎么存、方法怎么调、构造怎么写,一改全崩。组合则绕开这个陷阱,靠“持有对象”来复用行为,把依赖关系从编译期绑定转为运行期可控。
用字段持有替代 extends
不继承具体类,而是把需要的能力封装成独立组件,用 private 字段持有它:
- 想复用日志能力?不继承 BaseLogger,而是声明 private Logger logger = LoggerFactory.getLogger(...);
- 要验证参数?不继承 Validatable,而是持有一个 private Validator validator;
- 处理数据转换?不继承 ConverterBase,而是注入一个 Function<String, Object> parser;
这样,父类内部怎么重构(比如把内部缓存从 HashMap 换成 ConcurrentHashMap),完全不影响你的类。
接口定义契约,组合决定行为
把可变部分抽象成接口,让组合对象去选择具体实现:
立即学习“Java免费学习笔记(深入)”;
- 定义 PaymentProcessor 接口,而不是继承 AbstractPayment;
- OrderService 持有 private PaymentProcessor processor;,运行时注入 AlipayProcessor 或 WechatProcessor;
- 换支付渠道只需替换实例,不用改 OrderService 的继承结构或重新编译。
这比继承多层抽象类(如 AbstractPayment → OnlinePayment → ThirdPartyPayment)更轻量,也避免了“为扩展而继承”的过度设计。
拆分职责,避免大基类污染
当发现 BaseXXX 类越来越臃肿,里面混着权限、审计、序列化、缓存逻辑时,就是紧耦合的信号。组合的解法是:
- 把权限检查抽成 AuthorityChecker 接口,由业务类按需持有;
- 把审计日志抽成 AuditLogger,只在关键操作处显式调用 auditLogger.log(...);
- 把缓存逻辑封装为 CacheManager,不侵入业务类的继承链。
每个组件职责单一、可独立测试、可单独升级。业务类不再被迫继承一堆无关功能,也不再因某个父类方法改动而意外失效。
生命周期与所有权要明确
组合不是简单加个字段就完事,得理清谁创建、谁销毁、谁负责线程安全:
- 如果组件是无状态工具类(如 StringUtils),可直接 new 或用 static 方法;
- 如果组件有状态或资源(如 DataSource、HttpClient),建议通过构造函数传入,明确所有权;
- 避免在组合对象里 new 出来又不管理其生命周期,尤其涉及连接池、线程池等场景。
这点比继承更需主动设计,但也正因如此,组合才能真正解耦——你清楚每一环的控制权在哪。


















