为什么有人说面向对象编程已经过时,甚至是一种『反模式』?

面向对象编程(OOP)没有过时,但它已经从唯一的真理退化成了工具箱里的一个普通扳手。绝大多数人觉得 OOP 难用,其实根本原因不是 OOP 本身,而是继承被滥用了。你肯定在学校里学过:猫是动物,狗是动物,所以搞个 Animal 父类,然后 Cat 和 Dog 去继承。看着挺符合直觉对吧?但在实际的大型工程里,这种分类法简直就是灾难。我在大厂带项目的时候,见过无数次这样的场景:新人接手一个业务,想复用一段逻辑,发现这段逻辑在一个父类里。好,继承它。过两天发现还得复用另一个父类的逻辑,但 Java 不支持多重继承,怎么办?那就硬改继承链,或者搞出一堆莫名其妙的 Helper 类。这就导致了那个著名的 香蕉、大猩猩和丛林 的问题:你只想要一根香蕉,也就是一个简单的功能,但因为继承关系,你不得不把拿香蕉的大猩猩,以及大猩猩生活的整片丛林都拽进来。代码耦合度极高,改一个基类的属性,可能会导致几十层下面的子类崩盘。这种 脆弱的基类问题 在很多老旧的 Java 系统里简直就是鬼故事。现在大家更推崇的是 组合优于继承。你看现在的 Go 语言,或者 Rust,它们的设计哲学里压根就没有传统的类继承概念。Go 只有 Struct 和 Interface,Rust 有 Trait。这说明了什么?说明现代语言设计者已经意识到,强行把数据和行为绑在复杂的层级关系里,是一种心智负担。如果想深入理解为什么继承会把代码搞成一团乱麻,以及如何用更现代的方式去解耦,我强烈建议你去读一下 《重构:改善既有代码的设计》 这本书,特别是马丁·福勒讲 以委托取代继承 那些章节。虽然是老书,但里面的思想在今天依然是去油腻代码的神器。很多时候我们以为必须用继承解决的问题,用组合或者策略模式会干净得多。你的 CPU 不喜欢对象这点是做算法和高性能后端的人最有感触的。这也是为什么 面向数据设计 甚至是 函数式编程 最近这么火的原因。OOP 的核心思想是把数据和操作数据的方法封装在一起。这在逻辑上很通顺,但在计算机硬件层面上,这非常低效。现在的 CPU 很快,但内存相对来说太慢了。CPU 甚至要花大量时间等数据从内存加载到缓存里。为了让程序跑得快,你需要让数据在内存里 连续存放,这样 CPU 预取机制才能生效,缓存命中率才会高。但是在 OOP 里,特别是 Java 这种语言,你创建一个对象列表,实际上内存里存的是一堆 指针。这些指针指向堆内存里零散分布的各个对象。当你遍历这个列表时,CPU 就在内存里东奔西跑,疯狂地发生 Cache Miss。这叫 指针追逐,绝对的性能杀手。做游戏开发的兄弟对这个最清楚。Unity 为什么要搞 ECS 架构?就是要干掉传统的 OOP。在 ECS 里,数据是紧凑排列的数组,处理逻辑的 System 直接批量跑在数据上,快得飞起。如果你做数据分析或者机器学习,为什么 Python 这么慢,但 NumPy 和 PyTorch 那么快?因为 NumPy 底下是 C/C++ 写的,它操作的是连续内存块,它根本不搞对象那一套,它是纯粹的数据流。这里顺手推荐一个非常硬核的资源,YouTube 上 Mike Acton 的演讲 《Data-Oriented Design and C++》。虽然是讲 C++ 的,但那里面对数据布局的思考,对任何追求性能的工程师都是降维打击。看完你会明白,为什么有时候写一堆 Class 是一种反硬件的行为。并发环境下的噩梦这就说到你提问里提到的状态管理了。OOP 喜欢把状态藏在对象内部,通过方法去修改它。但在多线程环境下,可变状态 就是万恶之源。如果你有一个对象,被十个线程同时访问和修改,你就要加锁。锁加多了,性能下降,还容易死锁。锁加少了,数据竞争,结果出错。调试并发 bug 能让人掉光头发。这就是为什么 函数式编程 的概念这些年大举入侵主流语言。FP 强调 不可变性 和 纯函数。数据进去,新数据出来,原数据不变。没有副作用,不需要加锁。你看现在的 React,早期也是 Class Component,现在全都推 Hooks,推崇函数式组件。Redux 这种状态管理库,核心思想就是单一数据源、纯函数 Reducer,完全是反 OOP 的。在后端微服务里也是一样。服务最好是无状态的,数据流转清晰。如果你的服务里充斥着大量复杂的有状态对象,扩容和维护都会很麻烦。想深入理解这种思维转变,可以看看 《SICP》(计算机程序的构造和解释)。我知道这本书很难啃,名气大到吓人,但你不需要全读完,只需要看前面讲抽象和状态的部分,就能理解为什么把状态隔离出去是多么高级的设计。什么时候 OOP 还是不可替代的?说了这么多坏话,是不是 OOP 就要被扫进历史垃圾堆了?当然不是。如果你的业务逻辑非常复杂,而且非常贴近人类的自然认知,OOP 依然是最好的建模工具。比如你在开发一个企业级的 ERP 系统、电商交易流程或者复杂的仿真模拟系统。这时候,你的核心难点不是 CPU 缓存命中率,也不是极其苛刻的并发性能,而是 如何把现实世界复杂的业务规则映射到代码里,让原本复杂的逻辑变得可以被人理解和维护。这时候,领域驱动设计 配合 OOP 就非常强大。你定义一个 Order 订单对象,它里面包含 OrderItem 订单项,有支付、取消这些方法,这非常符合人类直觉。如果你用纯函数式或者纯数据驱动来写这种业务逻辑,代码可能会变得非常抽象,新来的同事可能得花好几个月才能看懂数据在管道里是怎么流转的。所以,在 业务逻辑建模 这块,OOP 依然是王者。还有 UI 开发。虽然 React 变函数式了,但你看 iOS 的 UIKit 或者 Android 的原生开发,甚至 Qt,依然是重度 OOP。为什么?因为 GUI 里的按钮、窗口、列表,天然就是对象,有状态,有继承。这种场景下,OOP 简直是天作之合。在这里我要推荐一下 《领域驱动设计:软件核心复杂性应对之道》,也就是著名的 DDD 蓝皮书。这是 OOP 的高阶玩法。当你觉得写 CRUD 写得恶心时,看看这本书,你会发现原来面向对象是为了解决业务复杂度的,而不是用来写 Getter/Setter 的。现在的趋势是 混合范式别搞二极管思维,非黑即白。现在的现实是,主流编程语言都在互相抄作业。Java 以前是纯 OOP,现在引入了 Lambda 表达式、Stream API、Record,越来越函数式。Python 既可以写 Class,也可以写脚本,还可以写函数式。Rust 这种新贵,抛弃了继承,用 Trait 实现多态,默认变量不可变,强迫你考虑内存布局,它是集大成者。作为技术人,我们现在的写法通常是这样的:宏观架构上,我们倾向于服务无状态化,数据流清晰化,这有点像函数式或者管道过滤器模式。数据处理上,比如 ETL、机器学习训练、高频交易,我们使用面向数据设计,用数组、Tensor,避免对象开销。核心业务域上,比如订单状态流转、权限模型设计,我们依然使用 OOP 和 DDD 来建模,保证逻辑清晰,易于维护。底层库设计上,多用接口而不是继承,多用组合。前两年我负责重构一个推荐系统的特征处理模块。老代码是 OOP 重灾区: 原来的代码是一堆 Java 类。有一个基类叫 FeatureExtractor,然后派生出 UserFeatureExtractor、ItemFeatureExtractor,下面又分 ClickHistoryExtractor 等等。每个类里面都有内部状态,处理数据的时候,这个对象拿一点数据,那个对象改一点数据。结果就是:慢。大量对象的创建销毁,GC 压力巨大。难以并行。因为对象里有状态,多线程跑的时候经常数据对不上。改不动。想加个新特征,得理清楚几层继承关系。重构后采用混合风格: 我们改成了类似函数式的数据流管道。数据进来,就是单纯的 Protobuf 或者 Struct,没有任何方法,只是数据。定义了一系列纯函数或者无状态的算子:extract_clicks, normalize, encode。通过配置文件把这些函数串起来。结果:性能提升了 5 倍以上。因为数据在内存里是连续处理的,而且没有对象创建开销。天然并行。把数据切片,丢给不同的核心去跑同样的函数,完全不用加锁。逻辑清晰。想加新特征,写个新函数插进去就行。你看,在这个场景下,OOP 就是反模式,函数式和数据驱动才是正解。为什么有人说 OOP 过时了?因为过去二十年,我们把 OOP 捧得太高了,把它当成了解决一切问题的银弹。不管是写个 Hello World 还是写个操作系统,都要先 new 个对象。这导致了大量的 过度设计 和 不必要的复杂性。现在的声音,是对这种矫枉过正的修正。给你的建议:不要死守 OOP。如果你发现自己在为了封装而封装,为了继承而继承,写了一堆没有任何业务含义的 AbstractManagerImpl,请停下来,想想要不要换个写法。关注数据流动。作为数据科学家或者算法工程师,数据是怎么在 CPU、内存、网络之间流动的,比对象之间的层级关系重要得多。多去了解 面向数据设计。拥抱函数式思维。尝试写纯函数,减少副作用,尽量让数据不可变。这会让你的代码 bug 少很多,睡个好觉。眼界放宽。去学一门在这个维度上完全不同的语言,比如 Clojure 或者 Rust,就算你工作中不用,它们也会彻底改变你写 Python 或 Java 的思维方式。OOP 并没有死,它只是回归了它应有的位置:一种处理复杂逻辑的有效手段,而不是编程的全部。这世上没有银弹,只有最适合当下的兵器。别被反模式这种词吓到了,那是理论家吵架用的,咱们干活的人,哪个好用用哪个。关于这块如果还想深入探究,除了前面提到的几本书,我还建议关注一下 Martin Kleppmann 的 《数据密集型应用系统设计》,也就是那本著名的 DDIA。虽然它是讲分布式系统的,但里面关于数据模型演变的讨论,能让你从更高的维度理解为什么我们正在慢慢弱化对对象的依赖。这本书真的是后端开发和架构师的圣经,没看过的赶紧去补课。保持开放的心态,代码才能越写越顺。共勉。