@
gowk 程序设计有一个范式( paradigm )问题。所谓范式,就是组织程序的基本思想,而这个基本思想,反映了程序设计者对程序的一个基本的哲学观,也就是说,他认为程序的本质是什么,他认为一个大的程序是由什么组成的。这和他对于现实世界的看法有关。显然,这样的看法不可能有很多种。编程作为一门行业,独立存在快 60 年了,但是所出现的范式不过三种——过程范式、函数范式、对象范式。其中函数范式与现实世界差距比较大——真的纯函数范式在目前的编程实践中其实很少有,绝大部分语言只是取了函数范式的一个子集,即可以把函数作为参数传递,不过这并不是函数范式的核心,在这里不多讨论,只谈一个历史趣闻,有兴趣的可以自行去进一步查资料:当年的大牛们认为,Haskell 是“美丽的,聪明的”设计,C 语言是“丑陋的,简陋的”设计,然而最后占据了世界主流的却是“丑陋的,简陋的”的设计。
而过程范式和对象范式可以视为对程序本质的两种根本不同的看法,而且能够分别在现实世界中找到相应的映射。
过程范式认为,程序是由一个又一个过程经过顺序、选择和循环的结构组合而成。反映在现实世界,过程范式体现了劳动分工之前“全能人”的工作特点——所有的事情都能干,所有的资源都是我的,只不过得具体的事情得一步步地来做。
对象范式则反映了劳动分工之后的团队协作的工作特点——每个人各有所长,各司其职,有各自的私有资源,工件和信息在人们之间彼此传递,最后完成工作。因此,对象范式也就形成了自己对程序的看法——程序是由一组对象组成,这些对象各有所能,通过消息传递实现协作。
对象范式与过程范式相比,有三个突出的优势,第一,由于实现了逻辑上的分工,降低了大规模程序的开发难度。第二,灵活性更好——若干对象在一起,可以灵活组合,可以以不同的方式协作,完成不同的任务,也可以灵活的替换和升级。第三,对象范式更加适应图形化、网络化、消息驱动的现代计算环境。
重复一遍对象范式的两个基本观念:
程序是由对象组成的;
对象之间互相发送消息,协作完成任务;
这就是对象范式的核心,并没有规定对象的标准,这里的对象只是一个抽象的概念,人为划定的,包含一定职责和功能的组件。对象之间互相“发消息”其实也只是一个抽象的概念,并没有规定“发消息”这一行为究竟该是什么样的,核心问题就在这里,这里埋下了一个大坑,为后来面向对象变成被扭曲成面向类编程留下了一个悬念。
历史没有假设,把 OOP 应用于编程语言实践并第一个“获得广泛成功”的语言是 C++,而上面说了,C++是基于 Simula 的思想,Simula 认为“发消息”是什么样的操作呢?向一个 Simula 对象中发送消息,就是调用这个对象的一个方法,或者称成员函数。那么你怎么知道能够在这个对象上调用这个成员函数呢?或者说,你怎么知道能够向这个对象发送某个消息呢?这就要求你必须确保这个对象具有合适的类型(方法),也就是说,你得先知道,哦这个对象是什么(类),才能向它发消息。而消息的实现方式被直接处理为成员函数调用,或虚函数调用。
而另外一边的 Smalltalk 在这一点上做了一个历史性的跨越,它实现了一个与目标对象无关的消息发送机制,不管那个对象是谁,也不管它是不是能正确的处理一个消息,作为发送消息的对象来说,可以毫无顾忌地抓住一个对象就发消息过去。接到消息的对象,要尝试理解这个消息,并最后调用自己的过程来处理消息。如果这个消息能被处理,那个对象自然会处理好,如果不能被处理,Smalltalk 系统会向消息的发送者回传一个 doesNotUnderstand 消息,予以通知。对象不用关心消息是如何传递给另一个对象的,传递过程被分离出来(而不是像 Simula 那样明确地被以成员函数调用的方式实现),可以是在内存中复制,也可以是进程间通讯。到了 Smalltalk-80 时,消息传递甚至可以跨越网络。所以各位,其实和消息队列类似的功能当年就已经存在了,消息队列 1 是为了解耦,2 是为了削峰错流,某种程度上消息队列不是实时的,然而 Smalltalk 的消息机制,某种程度上可以视为实时的。这也是它的先进性所在,我们希望解耦,但是我们不需要延迟。
很可惜,历史没有假设,Smalltalk 的直系继承者 Object-C 被 Steve Jobs 收入苹果公司,很长一段时间没有和公众见面。后来有一个也利用了该思想的专门为电信级通讯设计的语言 erlang,Actor 模式由它定义,其实就是那套消息机制,然而 erlang 是少见的纯函数式编程的语言,接受的人不多,也没流行开来。
后来就和大家知道的那样,因为 C++的流行,导致“类”这个概念变的无比重要,毕竟你要先知道对象的“类”,你才能确定,我能不能发消息给这个对象。于是类的概念变成了第一要素,OOP 至此被扭曲为 COP ( Class Oriented Programming,面向类程序设计)。
客观地说,“面向类的设计”并不是没有意义。来源于实践又高于实践的抽象和概念,往往能更有力地把握住现实世界的本质,比如 MVC 架构,就是这样的有力的抽象。但是这种抽象,应该是来源于长期最佳实践的总结和提高,而不是面对问题时主要的解决思路。过于强调这种抽象,无异于假定程序员各个都是哲学家,具有对现实世界准确而深刻的抽象能力,当然是不符合实际情况的。结果呢,刚学习面向对象没几天的程序员,对眼前鲜活的对象世界视而不见,一个个都煞有介事地去搞哲学冥想,企图越过现实世界,去抽象出其背后本质,当然败得很惨。
但是学术界一向高傲,传统上学术界一直都是理论定义的先驱,负有“指导众生”的义务,所以学术界仍然坚持这套基于类的 OOP 概念,但是工程界自从发现基于类的抽象其实对程序员的心智负担过高后,组合优于继承的思想就开始深入人心了。对象才是第一要素,对象并不依赖于类,对象的组合方式是彼此发消息,彼此发消息的方式也可以不依赖类,就算是传承自 C++的 Java 也搞出了接口这种方式——其实这种方式距离抛弃类就只有一步之遥了,我要知道对象有没有方法让我发消息,我检查接口定义就行了,我为啥需要类,Java 需要类来制造对象是历史惯性,不是必须。
历史让 C++走上了舞台,历史也终将让 COP 重新回到 OOP 的本来面目