架构设计中继承与组合的权衡核心在于语义关系、演化风险、解耦能力与职责粒度:语义上“是一个”用继承,“有一个”用组合;需评估父类是否支持继承;组合通过依赖注入和委托提升可控性与解耦;组合更利于单一职责与演进。

架构设计中继承与组合的权衡,核心不在语法能力高低,而在类之间真实关系是否稳固、职责是否清晰、未来是否容易演进。
先看语义关系:“是一个”还是“有一个”
这是最直接的判断入口,读出来是否自然就基本能定调:
- “订单有一个支付处理器” → 组合,Order 持有 PaymentProcessor 实例,职责分离明确
- “金毛犬是一种狗” → 继承,GoldenRetriever extends Dog,符合类型层级和多态使用场景
- “汽车有一个发动机”不能写成 Car extends Engine —— 这既违背常识,又让 Car 暴露 Engine 的内部细节,破坏封装
再看演化风险:父类是否真正支持继承
即使语义成立,“Dog 是 Animal”没错,但继承仍可能引入脆弱性。需确认三点:
- 父类 JavaDoc 明确声明 “This class is designed for inheritance”
- 关键方法未被 final 修饰,且无隐式契约(比如重写 toString() 导致序列化异常)
- 不暴露 protected 字段或内部状态(如 HashMap 的 table),否则子类一改就崩
像 String、LocalDateTime 这类类,哪怕语义上“日期时间是一个对象”,也不该继承——它们内部状态复杂,子类无法保证不变量。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
重点看解耦能力:组合如何提升可控性
组合不是简单堆字段,而是通过委托把责任边界划清楚:
- 构造时注入依赖:new DatabaseService(new MockJdbcDao()),测试无需启动数据库
- 运行时替换实现:service.setDao(new RedisCacheDao()),业务逻辑完全不动
- 避免继承链污染:super.super.method() 调用一旦父类重构,下游全挂;组合对象只调自己的接口,影响面窄
Spring 默认推荐构造注入而非继承,正是基于这个逻辑:控制权在使用者手里,不在父类设计者手里。
最后看职责粒度:一个类到底该承担多少事
继承容易让子类“被迫继承不需要的能力”,组合则天然支持单一职责:
- 如果一个类既要管理数据、又要渲染界面、还要处理网络请求——它大概率该拆,而不是靠继承多个父类(Java 不支持)
- 用组合,你可以让 ReportGenerator 持有 DataFetcher、Renderer、Exporter 各一个,每个只做一件事
- 每个成员变量对应一个明确契约,接口定义清晰,mock 和替换都直观
不复杂但容易忽略:组合不是堆字段,而是有意识地划分边界、定义委托关系。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










