组合与继承的选择取决于“关系本质”和“变化预期”:is-a关系且语义稳定时可用继承,has-a或字段易变时应优先组合;常量须分离至配置类或枚举,避免伪复用。

组合与继承不是非此即彼的选择,而是取决于“关系本质”和“变化预期”的设计判断。变量复用本身不是目标,真正要解决的是:如何让数据和行为在多个上下文中被安全、清晰、可维护地共享。
先看关系:是“has-a”还是“is-a”?
这是选型的起点,不满足这个前提,后续优化都容易走偏:
-
“is-a”关系才考虑继承:比如
AdminUseris-aUser,它们共享身份认证、权限校验等核心语义,且子类天然能替换父类使用(符合里氏替换原则);此时继承能自然复用父类字段(如user_id、created_at)和基础方法。 -
“has-a”关系优先用组合:比如
Orderhas-aPaymentInfo、has-aShippingAddress。这些数据结构独立存在、可能被Refund或Return复用,也常需单独校验或序列化——这时把PaymentInfo定义为独立类,由Order持有其实例,比让Order继承PaymentInfo更合理、更易测试。
再看变化:字段/逻辑是否稳定?
变量复用的脆弱点往往不在定义,而在演化。继承会让子类被动承担父类字段变更的风险;组合则把耦合控制在显式依赖处:
- 若某组字段(如
errors常量、DataMeta结构体)长期固定、跨模块共用,适合抽成独立类或枚举——它不参与行为继承,只提供结构约束,属于“静态复用”,组合调用即可。 - 若字段含义随业务演进频繁调整(例如
status从字符串变为状态机对象,或新增audit_log字段),继承会迫使所有子类同步修改;而组合允许Order按需升级所持有的PaymentInfo版本,其他类不受影响。
警惕“伪复用”陷阱
有些写法看似复用了变量,实则制造了隐性耦合:
-
滥用父类公共字段:在基类中定义
config或cache等通用属性,子类直接读写——这等于把全局状态塞进继承链,一旦某个子类误改,所有兄弟类都受影响。 -
继承只为共享常量:仅为了复用
MAX_RETRY = 3就让类继承一个Constants基类,违背单一职责,也破坏了类的语义完整性。这类值应放在模块级常量、配置类或枚举中,通过导入或依赖注入获取。 -
UI与数据模型混绑:如New/Edit界面共用同一UI类,靠继承添加不同
loadData()逻辑——结果是UI类既管渲染又管状态加载,变量(如formData)的初始化方式在子类中散落,复用时反而要反复检查各子类的构造逻辑。
推荐路径:组合打底,继承慎用,常量分离
多数场景下,变量复用最稳妥的落地方式是分层处理:
-
结构化数据:定义
UserInfo、Address等轻量数据类,不带逻辑,仅封装字段。各业务类通过属性持有其实例(组合),需要时传参或序列化。 -
行为与配置:将
API_TIMEOUT、ERROR_CODES等放入独立配置类或模块常量,避免污染领域模型。 -
真正需要继承的场景:仅限语义明确的“类型层级”,如
Animal → Dog/Cat、Document → PdfDoc/WordDoc,且确保子类不破坏父类契约(如不重写validate()使其跳过必填校验)。
不复杂但容易忽略。

















