AI Library · 知识库

# 语言哲学:面向对象与组合的边界

学习目标:从已有报告输出与装饰器示例出发,理解继承与组合在面对需求变化时的修改成本、扩展边界与责任分配。

前置阅读:将报告写入不同目标 与 组合与委托:输出组件增强。本篇不提供新实作代码,专注于从真实示例代码分析架构取舍。

# 从扩展新输出目标说起

在将报告写入不同目标中,核心报告生成函数 write_report 接收一个通用的输出目标参数。在需求变化为“增加文件输出”或在组合与委托中“为输出增加统一日志前缀”时,我们观察到两种截然不同的架构路线:

  1. 类继承体系(Inheritance):定义 ReportWriterBase 基类,派生 ConsoleReportWriter、FileReportWriter,并让 LoggingReportWriter 再次派生。
  2. 行为接口与组合(Composition):定义微小的写入协议(如 Go 的 io.Writer、Rust 的 std::io::Write、Java 的 Appendable/Writer),核心逻辑只依赖协议,通过装饰器包装实现增强。

# 继承带来的隐性耦合

当试图通过类继承扩展组件行为时,系统常面临以下结构性成本:

# 组合如何保持核心逻辑封闭

遵循开闭原则(Open-Closed Principle):

# 各语言的机制设计取舍

语言 面向对象核心取舍 为什么倾向组合而非继承?
Go 无类继承,仅支持结构体内嵌与隐式接口 组合加隐式接口保证包之间无强制继承耦合,代码可读性最高
Rust 无类继承,仅支持结构体与 Trait 组合 所有权模型无法容忍复杂继承层次带来的状态别名混杂
Java / Kotlin 支持单继承与多接口实现;Kotlin 原生内置类委托 by 框架层曾重度依赖继承,现代规范全面转向组合与 record 纯数据
Python 支持多重继承(MRO 算法) 动态语言下深度多重继承导致属性解析顺序极难追踪,现代库优先使用 Protocol 协议
C++ 支持多重继承与虚基类 菱形继承(Diamond Problem)的内存布局和析构顺序极为复杂,核心准则倡导非成员自由函数与组合

# 相关阅读