1. 为什么会出现面向对象——编程范式的根源
这些年我面试过不少候选人,发现一个比较普遍的现象:聊“什么是面向对象”,几乎人人都能脱口而出封装、继承、多态,可要是追问一句“为什么当年要发明这些东西?在它之前人们是怎么写代码的?”很多人就卡住了。这不是面试者的问题,而是面向对象编程(OOP)这套概念被讲得太像“标准答案”,很少有人从源头去理解它。
我第一次认真思考这个问题,是在维护一个用C语言写的业务系统的时候。那个系统大概有八万行代码,核心业务逻辑都堆在几个巨大的函数里,函数之间靠全局变量传数据。说实话,运行起来非常稳定,但谁也不敢轻易动它——你永远不知道修改一个全局变量的赋值逻辑,会影响到哪个角落里的函数。后来我用Java重新写了一个模块,第一次体会到“把数据和操作它的方法放在同一个class里”带来的掌控感,才发现原来写代码这件事,可以不用活得那么胆战心惊。
面向对象编程说起来玄乎,本质却非常简单:它把现实世界里的“对象”抽象成代码里的一个实体。每个对象内部既有自己的状态(数据),又有改变状态的行为(方法)。对象之间通过方法调用互相协作,而不是互相直接操纵对方的内部数据。就这么一个朴素的概念,为什么上世纪六十年代花了那么大力气才被提炼出来?因为我们站在今天回看会觉得理所当然,但当年的人们是先从过程化思维走过来的,要打破“代码等于一连串指令”这个根深蒂固的认知,需要一大批先驱踩出一条路。
1.1 面向过程编程遇到的现实困境
在面向对象出现之前,主流的编程范式是“结构化编程”。这种范式用顺序、循环、分支三种结构组织逻辑,核心方法叫“自顶向下、逐步求精”——一个大问题一层层拆分成小函数,每个函数完成一件明确的事情。这种思路放在解决单一算法问题上非常好用,比如写一个排序、算一个矩阵,逻辑清晰、效率也高。
但随着软件规模变大,问题就出现了。你可以把结构化编程想象成一条做菜的流水线:主函数是总厨,它指挥各个函数按部就班地洗菜、切菜、下锅。问题是,当菜品种类变多(业务逻辑变复杂),总厨要记住每一道菜的所有细节,切菜工要同时给十道菜切配,每个环节共享同一个案板(全局变量)。结果就是:代码高度耦合,某个切菜工改了一下动作,掌勺的、装盘的都可能出问题。
我自己在一家做ERP系统的公司待过几年,深有体会。旧系统里有个超级核心的数据结构,被二十多个函数直接修改。每次上线新版本,团队要做一遍全量回归测试,因为没人能确认改动的影响范围。这种“牵一发而动全身”的痛,几乎每个经历过老系统维护的程序员都刻骨铭心。面向对象解决的就是这个问题:把数据和操作打包成独立的“器官”,器官之间通过明确的边界协作,谁也不需要窥视谁的内部。
1.2 OOP想回答的根本问题
理解了前面的困境,就不难理解OOP想回答的根本问题了:当软件的复杂度超过某个阈值之后,怎么让代码仍然可以被人类理解和维护?
答案就是做“模块化”。面向对象把模块划分的粒度从“函数”提升到“对象”,每个对象是一个自包含的单元。封装保证了单元内部的独立性,继承和多态则让单元之间可以复用和协作。与其说OOP是一种编程技巧,不如说它是一种驾驭复杂度的组织策略。这个视角很重要,因为很多人写了很多年代码,仍然停留在“用class组织函数”的程度,没有真正理解OOP是为了让大系统可控——这也是为什么向小王这样的初学者,往往背熟了三大特性却依然写不好面向对象的代码。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从Simula到Smalltalk:OOP概念如何被一步步提炼
面向对象不是某一天突然被某个天才发明出来的,它经历了漫长的概念孕育期。整个历史脉络,基本上可以梳理成几个关键节点,每个节点都代表了当时人们为了解决具体问题而做的思考。
2.1 Simula 67:第一次出现类和对象
时间回到1960年代,挪威的两位科学家Kristen Nygaard和Ole-Johan Dahl正在做计算机模拟船舶、交通流之类的研究。他们要解决的问题是:用程序模拟一大堆相互独立又彼此关联的实体,比如港口里很多艘船,每艘船都有自己的位置、速度、装载状态,它们之间又相互影响。用传统的过程化思路写这种模拟程序,代码会乱成一锅粥,因为模拟对象的状态分散在各个全局变量里,很难组织。
他们做了一件很关键的事:把船和船上相关的数据、行为绑定在一起,做成一个“可实例化”的模板,这个模板就是后来的“类”,根据模板创建出来的一个个具体模拟对象,就是“对象”。Simula 67也因此被公认为第一个引入类和对象概念的编程语言。虽然它语法上还是结构化的底子,但概念层面的突破已经完成。
这个名字可能很多年轻程序员没听说过,但它的影响极其深远。后来Java、C++里的class、new、继承这些机制,往上追溯都能看到Simula的影子。顺便说一句,这两位科学家后来在2001年拿了图灵奖——当年的ACM对OOP的奠基性贡献,认可度非常高。
2.2 Smalltalk:把OOP打磨成完整哲学
如果说Simula发明了类机制,那Smalltalk就是让OOP真正“成型”的语言。它是由Alan Kay在施乐PARC实验室主持设计的,1970年代到1980年代逐渐成熟。Alan Kay自己有一段很著名的描述:“我想要的不是一种语言,而是一套以生物细胞为隐喻的计算模型。”在他的设想里,程序里的一切都是对象,哪怕数字、布尔值、类本身都是对象;对象之间通过“发消息”互相通信,没有任何对象能直接读取别的对象的内部状态。
这种设计催生了两个重要产物:第一是“一切皆对象”的理念,第二是集成开发环境(IDE)的原型。Smalltalk自带了图形化的开发环境、调试器、类浏览器,你可以在运行中的系统里修改类和对象——这在今天看起来很平常,但在当时完全是概念级的突破。现在很多人觉得一些现代IDE功能很炫酷,其实很多创意都是从Smalltalk时代传下来的。
Smalltalk对OOP的贡献不仅仅是概念,更关键的是它把“面向对象“从一种代码组织方式上升为一种思考范式。Alan Kay本人甚至不太愿意用“面向对象”这个词,他更愿意称自己的设计为“面向消息编程”。可惜的是,Smalltalk因为运行效率、商业生态等原因一直没有大规模进入工业界,但它对后续语言的影响是绕不开的。现在你在Java里调一个对象的方法,本质上就是给它“发了一个消息”,这个概念就是从Smalltalk来的。
我在写这篇文章之前,还特意找了一段Smalltalk的代码来看。说实话,第一次接触还是有点震撼的,那种“连if语句都是消息发送”的极端设计,让人意识到真正的OOP和我们日常写的Java/Python相比,还激进得多。国内很多教材一上来就讲类和对象,却极少有人提到Smalltalk说了“消息发送”这个底层逻辑,这也导致很多人的理解始终浮在语法层面。
3. 语言演进史中的关键角色:C++与Java如何塑造OOP
概念和实践之间,隔着巨大的鸿沟。Smalltalk把理论做得非常漂亮,但它没有解决工业落地的问题。真正把OOP带到千万程序员键盘下的,是C++和Java这两位功勋。
3.1 C++:让“类”走进高性能世界
Bjarne Stroustrup在1980年代开发C++时,目标是在不牺牲C语言性能的前提下,补充“类”能力。这里的“补充”两个字很关键——C++保留了C的全部语法和底层能力,同时把class、继承、多态、模板等OOP特性都加了进去。你依然可以像写C一样自由地管理内存,但你也可以选择用类和对象来组织代码。
这个设计其实是一种非常务实的折中:它用“兼容C”的方式大幅度降低了采用OOP的门槛,让一群习惯了C语言的开发者可以渐进式地拥抱面向对象。代价就是C++极其复杂,语法特性多到“一入C++深似海”,但不可否认,它是第一个让OOP在高性能领域(系统软件、游戏引擎、嵌入式设备)真正站住脚的语言。我年轻的时候做过的几个项目都是C++写的,每次都要小心翼翼地处理内存释放和对象生命周期,很磨练人,也确实能让人更深刻地理解OOP的底层原理。
C++还有一个贡献是促成了“模板”机制。模板不是标准的OOP特性,但它和OOP配合起来,让C++拥有了“泛型编程”的能力——写一份容器代码,能装各种类型的对象。后来Java的泛型、C#的泛型都是受了它的启发。所以你要理解OOP演进史,不能只看类和对象这条线,还要看它和类型系统、泛型怎么交织演进。
3.2 Java:把OOP变成程序员通用语言
Java出现在1995年,时机选得非常好。当时互联网刚起步,C++的复杂性和内存管理问题让大量企业级开发项目叫苦不迭。Java用两个杀手级特性解决了痛点:一是JVM带来的跨平台能力,二是垃圾回收(GC)让开发者不再手动管理内存。这两件事让Java迅速成为企业级应用开发的主流选择。
Java在设计上非常强调OOP纯度。它强制要求所有代码都必须写在类里,没有C++那种“游离于类之外的全局函数”,还取消了C++里让人头疼的多继承,改用接口来实现类似能力。这种“设计克制”让Java代码的可读性、可维护性都大幅提升——当然代价是更啰嗦,后来也被很多人调侃为“面向对象八股文”。但站在当时的环境来看,这个取舍是对的:大公司需要的是大量普通程序员能写出一致性高的代码,而不是让少数天才用花哨的语法炫技。
Java的崛起还催生了设计模式的大流行。GoF四人组写的那本《设计模式》正是基于Smalltalk和C++的实践经验总结出来的,而Java社区把它发扬光大。再到后来Spring框架的IoC容器把对象之间的依赖关系交给容器管理,OOP在Java世界里从“语言特性”演变成了一整套“工程方法论”。你在现在的Java项目里看到的面向对象,已经不单单是class和继承,而是一整套对象设计、依赖管理、分层架构的组合拳。
3.3 动态语言的OOP演绎:Python、Ruby与JavaScript
C++和Java让OOP成为主流,但OOP的演进并没有在静态类型语言里止步。Python和Ruby这些动态语言对OOP做了大量“软化处理”。Python允许你在运行时给对象动态添加属性,没有强制私有性(约定俗成用下划线),这种开放性让OOP更灵活,但也更考验人的自律性。
JavaScript的路线更特别。它一开始用的是“原型继承”(prototype-based),没有类,对象通过克隆其他对象来共享行为,后来ES6才加了class语法糖。很多写惯Java的人骂JavaScript的OOP不纯粹,但原型继承反而在某些场景下比类继承更灵活——它天然强调“对象之间的关系”,而不是“先定义模板再实例化”。在React出现之前,JavaScript的“面向对象”讲来讲去都是原型链那一套,后来大家索性开始拥抱函数式思想,这是后话。
我个人的看法是,不同语言对OOP的诠释没有高低之分,它们是在不同约束条件下做的不同取舍。理解这些演化的关键,是不要死盯“哪个语言最面向对象”,而是看“每个语言到底拿OOP解决了什么问题”。
4. 面向对象四大核心概念的底层逻辑
现在很多教程讲三大特性、四大特性,都把封装、继承、多态、抽象拆成一个个名词解释去背。但如果你想真正理解OOP的价值,需要把每个概念放回“它到底在解决什么问题”的语境里去思考。
4.1 封装:信息隐藏与边界意识
封装的核心价值是“信息隐藏”。它把对象的状态用private保护起来,外部只能通过公开的方法来访问和修改。这套机制看着简单,背后却是一种很深刻的边界意识——它人为制造了“实现细节”和“对外契约”之间的防火墙。
举个例子,假设你有一个表示银行账户的类,内部用一个double变量存余额。如果外部代码能直接改这个变量,很容易出现负数余额。但因为余额是private的,外部只能通过deposit和withdraw方法操作,你就可以在这两个方法里做校验,保证余额永远不会不合理。这个保护,就是封装带来的。
封装的价值在大团队协作里尤为突出。每个人只依赖别人公开的方法签名,不需要关心方法的内部怎么改,只要行为契约不变,谁都可以放心重构自己的实现。如果没有封装,代码之间就是赤裸的数据耦合,规模一上去基本就失控了。
不过封装也会被滥用。我在实际项目里见过一种“getter/setter泛滥症”:类里所有字段都生成getXxx/setXxx,外部代码把它当成公共数据桶在用。这种“假封装”比不封装更糟糕,因为它让你以为有了保护,实际上毫无边界。封装的关键是设计出对调用方有意义的行为方法,而不是简单地把字段包一层getter/setter。
4.2 继承:代码复用的双刃剑
继承解决的问题是“复用”——子类复用父类的属性和方法,同时还能扩展或覆盖。比如你写一个Animal基类,再让Dog、Cat都继承它,共同的行为写到基类里,不同的行为在子类里各自实现。
但继承是所有OOP概念里最容易被滥用的一个。经典的反面例子是那个“Square继承Rectangle”问题:数学上正方形是矩形的一种,但在代码里如果你让Square继承Rectangle,就会遇到麻烦——Rectangle有两个独立的宽和高,而Square要求宽高永远相等,在setWidth的时候还得去同步setHeight,直接违反里氏替换原则,代码到处都是bug。
我在一个物流系统的重构项目里也踩过类似的坑。最初设计了一个Transport基类,派生出Truck、Ship、Airplane,后来增加了一个“冷藏车厢”的需求,就有人试图在Transport上加冷藏相关字段,让所有子类都继承了用不到的东西,类层次开始变臭。后来我们改用组合加接口的方式重构:把“冷藏能力”抽象成一个单独的接口,让需要它的类去实现。这让我深刻体会到,“组合优于继承”这句话不是空话。
写代码这么多年,我的习惯是:除非你真的确定A永远是B的一种(is-a关系)并且基类相当稳定,否则优先用接口定义能力(can-do关系),用组合持有依赖。继承这张牌要慎重打,尤其是别超过三层继承深度,一旦超过,类与类之间的耦合基本会让你改一处崩三处。
4.3 多态:面向接口编程的基石
多态是OOP里最让人着迷的概念,它的核心能力是:同一段代码,可以作用于不同类型的对象,而行为由对象的实际类型动态决定。看一段Java代码:
java复制public class Animal {
public void sound() {
System.out.println("Some sound...");
}
}
public class Dog extends Animal {
@Override
public void sound() {
System.out.println("Woof!");
}
}
public class Cat extends Animal {
@Override
public void sound() {
System.out.println("Meow!");
}
}
public class Main {
public static void makeItSpeak(Animal animal) {
animal.sound();
}
public static void main(String[] args) {
Animal dog = new Dog();
Animal cat = new Cat();
makeItSpeak(dog); // 输出 Woof!
makeItSpeak(cat); // 输出 Meow!
}
}
这里的makeItSpeak方法完全不知道也不用知道参数到底是Dog还是Cat,它只依赖Animal类型的sound()方法。真正执行的是Dog的sound还是Cat的sound,由运行时实际传入的对象决定。这个“晚绑定”机制,让代码的扩展性发生了质变——以后再来一个Bird,只要让它继承Animal并重写sound(),makeItSpeak一行都不用改。
多态是“面向接口编程”的底气。只有代码依赖的是抽象类型(接口或基类),你才能在不改动上层逻辑的前提下,自由替换底层实现。Spring的依赖注入之所以好用,底层靠的就是多态;各种设计模式里大批大批的代码,底层机制也是多态。明白了多态,你才算真正明白了OOP为什么能对抗变化。
4.4 抽象:抽象类和接口的演进
抽象常被放在最后说,但其实它是OOP里最底层的思想。抽象就是提取事物的本质特征,忽略非本质细节。Animal就是一个抽象:它描述了“能发声的生物”这个本质,但不会具体到叫声是什么样的。而到了Java里,抽象被语言工具化了,分成了“抽象类”和“接口”两个层次。
Java最初的设计里,它们的区别很简单:抽象类可以有字段和已实现的方法,但接口只能有方法签名(常量除外),类只能单继承抽象类,但可以实现多个接口。后来Java 8在接口里加入了default方法,Java 9又允许接口里写private方法,Java 16/17又引入了sealed class和record——这些变化说明语言设计者也在不断调整抽象机制的边界。现在很多现代Java项目里,抽象类用得越来越少,接口+组合成了绝对主流,因为组合比继承更灵活,尤其在跨层次复用行为时,接口的威力是抽象类完全比不上的。
你去看那些好代码和烂代码,最本质的区别往往不是缩进、命名,而是抽象的层次感。好代码的抽象是均匀的:每个层次的代码只关注它这个层次该关心的事。烂代码则恰恰相反,要么抽象过度(到处都是接口和一层套一层的包装),要么抽象不足(业务逻辑全堆在一起,函数比命还长)。
5. 工程实践中的OOP演进:设计模式到SOLID落地
概念讲再多,落不了地也是空谈。OOP真正对整个软件工业产生革命性影响,是在它被系统性应用于大型工程之后。这个过程中沉淀下来的一批方法论,比语言本身更能代表OOP的实践价值。
5.1 设计模式:OOP经验教训的“招法库”
1994年,Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides四人合著了《设计模式:可复用面向对象软件的基础》,收集了23种在面向对象设计中反复出现的经典解决方案。这本书在Java/C++社区火到不行,几乎人手一本。
设计模式本质上是“在特定场景下被验证过的对象协作结构”。比如策略模式就是把算法族封装成一组可互换的策略对象,让调用方在运行时决定用哪种策略;观察者模式定义了一对多的依赖关系,让多个观察者对象能自动收到主题对象的状态变化通知。新手看着觉得这玩意儿不过就是把A类拆成好几个类,但真的自己动手写过一个有状态、有变化、有扩展需求的系统之后,才会从骨子里认同这些模式的价值。
不过设计模式也有很大的副作用。国内Java圈有一段时间流行“模式崇拜”,写个不算太复杂的业务代码也要硬套三五个设计模式,结果就是类数量爆炸、代码变得极其晦涩。我自己接手过这样的项目:读代码的时候仿佛在猜谜,要一层层扒开装饰器、策略、工厂,才能找到真正干活的那一行。写代码是给人看的,如果为了套模式而牺牲了可读性,那就本末倒置了。
5.2 SOLID原则:让OOP设计有章可循
如果说设计模式是招法,那么SOLID原则就是心法。这五个原则分别是:
- 单一职责原则(S):一个类只负责一件事,只有一个引起它变化的原因。
- 开闭原则(O):对扩展开放,对修改关闭,尽量用新代码实现新需求,而不是修改老代码。
- 里氏替换原则(L):子类必须能替换父类,并且程序行为不发生变化。
- 接口隔离原则(I):客户端不应该依赖它不需要的接口,接口要小而专。
- 依赖倒置原则(D):高层模块不依赖低层模块,两者都依赖抽象;抽象不依赖细节,细节依赖抽象。
这些原则每个都不是什么高深理论,但放在工程里几乎条条都是血泪教训换来的。比如开闭原则,说白了就是希望你在加需求时用扩展的方式,而不是去改动已经验证过的老代码。我在团队里推行过一段时间System Design Review,发现新手最容易违反的是单一职责:一个类塞进太多不相干的职责,改一个需求殃及一片。每次做代码评审,我都会问“为什么这个类要关心这些事情”,问几次,大家自然就会开始有意识拆分。
SOLID原则不是银弹,但它提供了一套非常实用的“设计体检标准”。你写完一个类可以对照着问自己:我的类职责单一吗?我到底是依赖接口还是依赖具体实现?子类能不能替换父类?问完一遍,很多设计味道很自然地就被修正了。
5.3 依赖注入与框架化:OOP协作方式的重构
OOP演进到后期,出现了一个非常关键的转折——对象之间的协作关系,从“自己创建依赖”变成了“外部注入依赖”,这就是依赖注入(DI)思想的精髓。
举例说明:假设ServiceA需要一个Logger,传统写法是直接在ServiceA里new一个Logger,这导致ServiceA和Logger的实现强绑定。使用依赖注入之后,Logger由外部(容器)创建好,再“注入”给ServiceA,ServiceA只需要声明我需要一个Logger。这一个小小的反转,带来了巨大的可测试性:单元测试时可以注入一个Mock的Logger,完全不需要动ServiceA的代码。
Spring框架把这一套发挥到了极致,它的IoC容器负责维护所有对象的生命周期和依赖关系,开发者只需要在配置里声明谁依赖谁,剩下的交给框架处理。这也让企业级Java开发从“用OOP写代码”升级成了“用OOP组织系统”。Spring的成功证明了OOP不只是一门语法课,它更大的意义是把软件系统的结构化成了一种可控的架构图。看着一张大系统的依赖图,比看一万行代码更容易理解它整体长什么样。
5.4 贫血模型与DDD:对OOP建模的反思
广泛应用必然带来反思。Java社区在早期做业务系统时,有一种非常常见的做法:数据库表对应一个实体类,实体类里只有一堆字段和getter/setter,业务逻辑全部写在Service层里。这种设计被Martin Fowler称为“贫血模型”——对象只是数据的载体,没有任何行为,系统的所有智慧都集中在外部的Service层。
贫血模型写起来很顺手,但它严格来说根本不面向对象:数据和操作又被割裂了,只不过割裂的单位从“全局变量+函数”变成了“实体类+Service层”。于是后来有了领域驱动设计(DDD),它强调把业务逻辑放在领域对象内部,围绕“聚合”、“值对象”、“领域服务”重新组织模型。DDD没有否定OOP,恰恰是把OOP的凝聚力和封装思想重新拉回到业务建模的核心位置。
我这几年做微服务架构时,越来越感受到DDD的实用性。传统的面向数据库设计,业务逻辑被架在数据库表结构之上,改业务本质就是改表。而DDD会先分析业务领域里的概念、规则和关系,然后用对象显式建模,数据库结构反而是可以后置考虑的实现细节。这种思维顺序的调换,只靠语言层面的OOP是做不到的,得靠架构方法论的配合。
6. 面向对象的争议与未来走向
任何技术发展到一定阶段都会遇到批评者,OOP也不例外。认真听听这些批评,对理解OOP的边界反而有帮助。
6.1 批评的声音:继承滥用、封装失效与贫血模型
对OOP最常见的批评集中在这几点:第一,继承被滥用,导致类层次僵硬、难以维护;第二,封装经常流于形式,大家写一堆getter/setter,数据和操作在本质上还是分离的;第三,很多“面向对象”代码其实还是过程式思维,只不过把函数搬进了类里,多了一层class的外壳。
这些批评都有道理。尤其是现在很多业务系统的代码,类的颗粒度是按照数据库表对齐的,一个“用户类”必然有userId、userName、getUserId、setUserId……写起来机械味十足。但是有没有想过,这不是OOP本身的问题,而是应用方式的问题。如果你把OOP当成一种组织思想的工具,而不是把“写了class就算面向对象”当成KPI,那些被批评的现象完全可以规避。
我在跟同行交流时经常说,OOP最成功的不是类、继承那一堆语法机制,而是它把“思考怎么组织代码”这件事提升到了一个前所未有的高度。在OOP铺开之前,大部分程序员写代码其实不做太多结构设计;OOP出现之后,大家开始认真思考代码的边界、依赖、复用、抽象。这个思维习惯带来的收益,远大于语言特性本身。
6.2 函数式编程的挑战:OOP并非唯一解
过去十几年,函数式编程重新崛起,对OOP构成了不小的挑战。函数式强调不变性(数据一旦创建不可修改)、纯函数(同样输入永远得到同样输出)、函数是一等公民。这些特性在并发编程、数据流水线、可预测性方面,确实有OOP无法比拟的优势。
今天主流的语言几乎都在走“多范式融合”的路线。Java从8开始引入Lambda和Stream,JavaScript有强大的函数式库,Python本身就支持函数式特性。你很难再说一个语言是“纯面向对象”或“纯函数式”了。这不是OOP的失败,而是一个生态成熟后的必然——没有任何一种范式能包打天下,混合使用不同范式的优势,才是工程师该有的态度。
在我自己写代码的习惯里,领域建模和系统架构选OOP,数据转换和中间处理逻辑选函数式,两种风格组合起来用反而很顺手。类是对象的蓝图,函数是动作的单元,它们不是对手,而是工具箱里的两种工具。
6.3 OOP的现代化:从“语法特性”到“思维习惯”
现在再回看OOP这几十年的演进,我认为最大的变化是:它正在从一种语言特性变成一种思维习惯。新生代的程序员从一开始接触的就是高度成熟的面向对象语言和框架,class、对象、多态对他们而言像空气一样自然。真正的挑战不再是“会不会用class”,而是“有没有面向对象的设计直觉”。
Java最近这些年引入了record(不可变数据载体)、sealed class(受限继承)、增强的switch表达式,都说明语言设计者在尽量减轻开发者的语法负担,让开发者把注意力放在架构思考上。面向对象编程不会消失,它会一直演化,变得更轻、更灵活、更贴近人的思维习惯。
对我个人而言,写了十几年面向对象代码,最大的体会是它教的不仅是“怎么写”,更是“怎么想”。我在设计一个新系统时,脑子里冒出的第一个念头从来不是“我要写几个类”,而是“这里面有哪些独立的事物、它们之间有什么责任边界”。想清楚了这些,代码怎么组织反而是水到渠成的事。这种思维方式的养成,也许比记住任何一门语言的语法都更重要。如果你还在为一堆class怎么设计而纠结,不妨先放下代码,拿笔在纸上画一画对象之间的协作关系,你会发现,困扰你的很多问题,其实在动键盘之前就已经能解决大半了。
