面向对象编程的陷阱(2009)[pdf]
面向对象编程既被当作一种概念模型,也被当作一种性能权衡来审视;许多人认为它对标识、可变状态和继承的强调,往往在没有明显收益的情况下增加复杂度,相比之下不如更简单的面向数据或函数式设计。评论者将 Alan Kay 最初的消息传递式对象愿景,与主流 OO(如 Java、C++)的实际做法进行对比,指出诸如对缓存不友好的对象图、脆弱的继承层次,以及“万物皆对象”思维的过度使用等问题。一个反复出现的主题是,没有单一范式足以胜任一切:高效系统会混合对象、函数以及关系型或基于集合的模型,仅在封装状态和多态行为真正有帮助的地方选择性地使用 OO,例如 UI 代码的某些部分。
OO“真正是什么”
- 多条评论认为维基百科“数据 + 方法放在一起”的定义具有误导性。
- 一派认为其本质是标识 + 可变状态:即使字段不同,两个对象也可以是“同一个”。
- 另一派强调消息发送 + 动态分派:不同对象对同一消息作出不同响应;可变性和标识很常见,但并非必需。
- 两种观点都同意,内存布局只是附带现象,并非定义本身。
OO 与其他范式(标识、集合、关系)
- 一个反复出现的主题是:OO 鼓励“意向性标识”(你发明对象/服务,并把数据/行为塞进去),这可能会膨胀系统复杂度。
- 相比之下,关系型/逻辑方法强调“外延性标识”(实体由值/元组定义;标识从属性中涌现)。
- 有人指出,许多开发者深陷 OO 之中,以至于把它视为自然,然后笨拙地把关系型/函数式/逻辑系统包裹进 OO 抽象里。
继承、组合、接口
- 对把类继承作为主要设计工具的批评很强烈;组合、接口/trait/typeclass 和委托通常更受青睐。
- 也有人为继承辩护,认为它是强大而便利的扩展与 UI 定制机制,尤其是在谨慎约束下(final 类/方法、清晰的扩展点)。
- 讨论集中在委托 + 泛型是否能匹配继承的表达力和易用性;大家一致认为,滥用继承很容易导致脆弱的设计。
OO 在 GUI 与后端中的适用性
- 许多人认为 OO 很适合传统 GUI 工具包(带状态和行为的控件、层级结构)。
- 现代 UI 框架(React、Elm、Compose、SwiftUI)更偏向声明式/函数式或响应式风格,尽管批评者认为它们底层仍依赖隐藏的、类似 OO 的状态。
- 关于 React hooks 的讨论:表面上感觉是函数式,但它们打破了纯 FP 规则,并依赖不寻常的语义。
性能与面向数据设计
- 幻灯片被认为展示了:以对象为中心的对象图会损害缓存局部性和预取能力,相比之下,面向数据的布局(例如数组/向量)更有利。
- 有人进一步概括:模块化和抽象往往要以原始性能为代价;面向数据设计是在性能关键代码中对这种代价的反击。
实用主义与多范式使用
- 多位评论者主张多范式实践:某些部分用 OO,其他部分用 FP/关系型/逻辑方法。
- 极端立场(“一切都是对象”、“一切都是不可变的”)被视为适得其反;应当由问题本身决定所采用的范式。