BT

如何利用碎片时间提升技术认知与能力? 点击获取答案

对象已死?

| 作者 徐昊 关注 2 他的粉丝 发布于 2011年5月11日. 估计阅读时间: 15 分钟 | 都知道硅谷人工智能做的好,你知道 硅谷的运维技术 也值得参考吗?QCon上海带你探索其中的奥义

最近常有一种说法,就是我们如今面临着另外一场编程模型的变革,面向对象技术已经处在被淘汰的边缘,函数式语言会取代面向对象技术成为主流方式,甚至出现了面向对象已死的言论。作为一个硬核函数语言的狂热者,我个人当然希望函数式语言可以一统天下,成为主流之选。但是不是应该把对象技术和函数技术对立起来,说式后者取前者而代之,我个人认为,这和如何看待面向对象技术有关。

做为工程实践的对象技术

在这个年代,大家有一种神圣化面向对象技术的倾向,很多人都把对象技术奉为高深的思想和理论。但实际上,面向对象技术仅仅一种工程实践而已,它是依托于其他技术而存在的一种实践,本身并不是一种完备的计算模型。

在计算机科学发展的早期,对于计算机的非数值计算应用的讨论,以及对于可计算性问题的研究和发展,大抵确立了几种的计算模型:递归函数类、图灵机、Lambda演算、Horn子句、Post系统等等。其中递归函数类是可计算性问题的数学解释;Horn子句是prolog这类逻辑语言的理论基础;lambda演算成为了函数式语言的理论基础;图灵机是图灵解决可计算问题的时候所设计的装置,其后成为计算机的装置模型,与图灵机相关的自动机以及冯诺依曼结构,成为了命令式语言的理论基础。

因此当我们谈及函数语言和命令式语言优劣的时候,我们实际上是在讨论其背后的计算模型——也就是lambda演算和冯结构装置操作——在执行效率和抽象层次上的优劣。

而面向对象技术则比较尴尬了,其背后没有一个对应的计算模型(80年代的时候曾有人研究过,Pi演算是个备选,但是这个模型更多的是在并发对象领域的语义,而不是通常意义上的计算模型)。它有点类似于“最佳实践”,在不同的计算模型上有着完全不同实现方式和含义。因此对比对象技术和其他技术的时候,搞清楚到底是哪一种面向对象就变得格外重要起来。

两种不同的面向对象

目前流行的对象技术,实际上有两个截然不同的源头。它们分别在两个完全不同的计算模型上发展起来,但是都顶着“面向对象”这个帽子。

第一种对象技术出现的较晚,在1979年以后。它是以抽象数据类型(ADT,Abstract Data Type)为源起,发展出来的面向对象技术。也就是首先被C++所采用的面向对象技术。

C++作为“更好的C”,继承了C语言对于程序的看法,也就是数据抽象(Data Abstraction)和过程。面向对象技术在C++中,是作为一种更好的数据抽象的方式而存在的。

数据抽象在这类面向对象语言中是一种关键的抽象方式。所谓数据抽象,在计算机发展的早期是一种非常关键的技术。众所周知,计算机在装置模型上是一个存储和一组指令集,而二进制的存储实际上是没有任何类型表示的。整数,浮点这些操作必须通过相应的约定,再以指令集的形式进行支持。而随着计算机的发展,简单的数据类型显然已经不能满足应用的需要。这时候一种灵活且有效的类型系统,就成了一种自然的追求(直到80年代初,类型系统都是计算机科学研究的重要方向之一)。

在C++中(以及后来的Java和C#),对象是一种构造数据类型的方式,把每个“类”看作一段存储(状态)和操作(方法)的集合。“类”作为已经存在的类型系统的一种扩展(这一点在C++中体现得尤其强烈)。在这类语言种,“类”(class)实际上代替了“对象”(object)成为了头等公民。构造一个更好的类型系统,是这种面向对象技术所要解决的问题。与其说是面向对象,不如说是面向类或面向类型的。

从计算语义上说,这类对象技术仍然是装置的操作语义,和面向过程的没有实质上的区别。唯一的不同是,被这种对象语言操作的机器,可以借由对象技术扩展机器所支持的类型。这种面向对象技术是过程技术的一种发展,虽然在抽象层次上没有什么太大的提高,但在实践上已经是巨大的进步。

另一种对象技术出现的很早,大概在60年代末就出现了,直到80年代初还有发展。但是很长一段时间内并不是太主流的做法,反而并不太为人所知。

在函数式语言里,因为高阶函数(High Order Function)的存在,数据可由函数来表达。这就是函数语言里一个非常重要的观点:Data as Procedure。在函数语言中,可以构造一种非常类似于对象的高阶函数:

(define (make-user name age sex)
  (define (dispatch message)
 	(cond ((eq? message 'getName) name)
       	((eq? message 'getAge) age)
       	((eq? message 'getSex) sex))
       	(else (error 'messageNotUnderstand))))
  dispatch)
 
(define vincent (make-user 'Vincent 30 'Male))
(vincent 'getName)

如上面的Lisp代码所示,可以借由返回一个dispatch函数,将基本数据组合成一个更复杂的数据对象,而通过高阶函数的后续调用,可以使用相应的选择器(selector)与数据对象交互。这种风格的数据抽象被称作“消息传递”(Message Passing),是早期面向对象技术的雏形,无论是Smalltalk还是CLOS都是以这种技术为蓝本,设计的对象系统,包括后来的Ruby,实际上也是这种模型的一个发展。

因此实际上,就算在函数式语言上面,我们仍然可以通过引入这种对象的形式,对函数进行相应的模块化和局部化。这种形式的对象与函数本身没有任何差别,因此这种类型的对象系统,被称作“方便的接口”,用于简化对象的函数的访问和调用。

在函数式语言里,另一个非常重要的概念就是“副作用”(Side effect,即函数可以修改某个存在的状态)。像Lisp并不是纯函数语言,因此是允许状态修改的。因此对象技术除了可以被看作函数局部化和模块化的方法之外,还可以看作副作用局部化的一种方式。采用这类面向对象技术的语言,通常被称作动态面向对象语言。

这类对象语言通常都会保持一些函数式语言的特性,比如lambda的各种变体,比如较容易的函数组合,比如curry,比如高阶函数。而且由于这类对象系统是从函数式发展出来的,也更加推崇一些副作用小的,利用高阶函数的对象设计方法。比如,不变体(Immutable object)回调等等。

计算语义上,无副作用的对象系统实际上和Lambda演算享有同样的计算语义。而带副作用的本身只能被看作一种坏的实现,在函数上都没有明确语义。仅仅能够看作对于副作用的局部化和模块化。

以 上,我们简单地看了一下两种不同的“面向对象”技术。其中一种是用来解决如何构造更好的类型系统的,另一种是用来对函数和副作用进行有效模块化和局部化的。如果单以这两种面向对象技术和函数式语言去比较,实在不是一个层次的东西。那么为什么我们最近能够听到这么多函数和对象的讨论呢?

新的发展

静态类型函数语言

最早的函数语言是不太在意类型的,因为有Data as Procedure的存在,lambda演算可以通过把参数类型抽象成另一个高阶函数来绕过函数参数类型问题(把参数也变成lambda,每个函数都看作参数和函数体的高阶)。然而随着形式化类型系统在理论上的发展,把lambda演算扩展为typed lambda演算自然就是一种很自然的推论。

随着在此基础上发展出来的ML族和Haskell语言的日渐成熟,以及代数数据类型(algebraic data type)的引入,这些语言可以较为容易地构造出非常复杂的类型系统。而且伴随着类型推演和类型计算的引入,类型间复杂的关系也可以较为容易表达。由此,静态类型函数式语言也开始挑战以对象为基础的类型系统构造方法。

实际上这里函数语言的挑战是类型系统之争,而非面向对象和函数语言之争。因此,消息传递类的对象语言根本不在讨论之列,而对于静态类型面向对象语言而言,除了C++外(而对于C++,面向对象仅仅是构造类型系统的一种方式,另一种则是著名的范型编程。我仍然相信,在语义上静态类型函数语言会胜过C++很多,但是弹性和表现力C++并不会差太多),其他主流语言如Java和C#,类型系统的已经被限制在一个相对简单的范畴内,说完败也不为过。

主流平台也为需要处理复杂类型系统的开发者提供了不同的选择,比如.NET平台上的F#。以及JVM上的Scala。都是在主流平台上引入静态类型函数语言的一些特征,来简化复杂类型系统的构造。

并发编程/并行计算/多核编程

Lisp并不是一个纯函数语言,它允许有副作用存在。后来发展了一些严格的纯函数语言,严格禁止副作用。也就是所有变量都和数学中的变量具有相同的语义,不能修改。然而计算机程序终归是要处理状态变化、输入输出这些不具有函数语义的操作的。一些纯函数语言开始引入了更精巧的方式来管理状态,比如Monad。Monad的传递性使得副作用的扩散在函数中变得更明确可见。

这种方式本来是用来解决纯函数语言内副作用处理的一种技巧,但是恰好赶上Intel受制于生产技术,无法再通过提高单核频率以追赶摩尔定律,必须通过集成多核的方式来制造更快的CPU。多核CPU作为一种新的事物,给计算机界带来了新的恐慌,大家觉得有必要使用一种新的编程模型以充分利用多核的优势。

而第一个尝试的方案就是将计算分布到多个CPU上,也就是利用多核进行并行计算。这时候,纯函数式语言对于副作用的处理,恰好给多核编译器提供了一个理想的优化方式:即所有无作用的函数皆可以随意分布到多核上,而带副作用的函数则无法分布。通过对于类型系统的简单识别和标注,就可以自动地将纯函数式程序编译为支持多核的程序。这在一段时间内,形成一种函数式语言是自动适应多核的,而面向对象程序则需要重写的印象。一时间内,函数与对象之间的选择实际上变成了多核和单核的选择。

好在还有Amdahl's law存在,事实也证明除去一些特定的应用场景,自动编译为支持多核并行的函数式程序并不快多少,而转化为纯函数程序的成本却高出不少,同时大多数纯函数语言都带有学术性质,对于团队开发并不友好。在加上JVM和.NET CLR对于多核都做出了一些回应。因此除去一些计算密集型应用,纯函数语言并没比面向对象好多少。

峰回路转的是,由消息传递风格发展出来的actor模型,利用操作系统的进程/线程特性,在一个合理的粒度上很好地利用了多核的能力,简化了并发编程。虽然第一个著名的实现是Erlang的actor系统,但是由于消息传递风格和面向对象模型相去不远,很快就在各种面向对象语言中有了类库支持。虽然利用当代函数语言的语法特性,actor可以实现得更简洁,但是对象对于副作用和状态的封装,更好地解决了在并发环境下对于共享状态的操作,反而有了更好的发展。

以上,我们看了函数式语言中两个新的发展,以及围绕这些发展涉及的一些“对象v.s.函数”的讨论。正如本文一开始所说,对象技术作为一种工程实践,其发展总是依托于其他更基本的计算模型的演化的。函数语言的发展,使得我们对于对象的认识和理解有了更深更好的认识。而对象作为函数的“方便的接口”总会在新的发展中,让我们更加便利的享有函数式和其他计算模型发展的成果。

回到本文最开始的讨论,函数的发展会的确会促使一些对象技术的消亡,但也会产生新的对象技术。或许更好的理解和掌握函数,类型系统才是真正掌握对象技术的捷径,也未可知。

关于作者

徐昊,ThoughtWorks中国区首席技术专家,ThoughtWorks全球技术策略顾问(TAB),TW中国首席咨询师。BJUG(Beijing Java User Group)和AgileChina创始人。从2003年起开始实践极限编程等敏捷方法,2005年开始,多次以敏捷教练的角色帮助国内外多个团队实施极限编程,Scrum和FDD等敏捷方法,敏捷交付和敏捷项目管理经验极为丰富。目前主要致力于大规模团队(300-500人)内的敏捷实践和管理再造,以及对企业级技术应用趋势和技术战略的研究。


感谢张凯峰对本文的审校。

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家加入到InfoQ中文站用户讨论组中与我们的编辑和其他读者朋友交流。

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

这篇真的很精彩,尤其是第一段 by zhang 3

“在这个年代,大家有一种神圣化面向对象技术的倾向,很多人都把对象技术奉为高深的思想和理论。但实际上,面向对象技术仅仅一种工程实践而已,它是依托于其他技术而存在的一种实践,本身并不是一种完备的计算模型。”

作者所提到的计算模型中,有一种是业界现在比较忽视的,那就是“Horn子句”,以horn子句为基础的prolog语言在“面向对象”的构造中,具有热门的函数式也无法比拟的优点。

我在对“统一编程语言”的(独立?/民科?)研究中,采用了prolog做为构造新语言的基础工具,前一段时间写出来三篇关于面向对象一个连载(未完),是在用prolog制作新语言IDE时,为了编程的方便,创建语法机制时所总结的,一个初步的探索。

暂定的基本框架是:
1 世界是事实的总和,而不是事物的总和(拟维特根斯坦语,没办法,这家伙说话太精炼了)
2 事件是事实空间的变化模式。
3 动作是引发事实空间的变化的模式。
4 对动作的执行改变事实空间。
5 响应是事件引发动作的过程。

面向对象的解构系列(目前共三篇,未完):

www.douban.com/note/142604730/
www.douban.com/note/142620392/
www.douban.com/note/143944769/

精彩!作为在学生,醍醐灌顶。 by 何 世友

面向对象只是一种实践!

去看看《黑客与画家》吧 by haoxiang zhang

去看看《黑客与画家》吧, 看看大师的看法!

面向对象贴合实际 by 张 宝华

曾经看过一本书,讲的是设计模式,他用设计模式的思想讲四大名著里面的几个故事!! 用道家的说法,道生一,一生二,二生三,三生万物。混沌理论讲,世界是由简单的规则演变而来,由物质角度讲,世界是由简单的分子原子组成。有简单构成复杂,由相同构成不同,是世界的原理。计算机是用来虚拟,描述世界事物的一个工具。只有贴合实际才可以描述的准确,我的浅见!!

标题党? by Zou Hao

为什么对象已死?

Re: 标题党? by zhang 3

面向对象就没活过。。。。哈哈哈

太精彩了,我是你的粉丝! by 田 乐

能请您给签个名么?这篇完全没有废话,分析的非常清楚,忍不住要多读几遍。

文章很不错,就一点想纠正下 by Jeffrey Zhao

希望作者可以重新审视下C# 3.0及以后的C#,我觉得作者对C#的看法依旧停留在2.0及以前。

太到位了. by kung shieldern

忍不住都开始崇拜了

Re: 文章很不错,就一点想纠正下 by liu feng

作者只不过是在说明问题,与C#x.0无关。

Re: 文章很不错,就一点想纠正下 by 张 逸

即使只从实践的角度来看,我比较看好多语言的开发模式。

Re: 这篇真的很精彩,尤其是第一段 by Tao He

很有意思的话题。
希望作者能够提供推荐一些参考阅读~~

Re: by fei yin

再完备的计算模型做出来的东西也是要给人用`面向对象从拉近软件世界与现实世界确是很好的方法`缩短了人和代码之间的鸿沟`

我看 by - 博文 1公子

我看是你死了

Re: 文章很不错,就一点想纠正下 by Jeffrey Zhao

说明问题用的例子不妥当,自然有关。

ThoughtWorks中国区首席技术专家 by Shooter Evan

不愧是 ThoughtWorks中国区首席技术专家

纯粹是标题党! by xiaolong xing

哈哈,娱你一乐

看到的内容和我看标题时想看的东西是截然不同的。到底是OO还是PO,非要给出个是与非,本身就是浅薄的,作者当然不会具体讨论这个问题。

Re: 这篇真的很精彩,尤其是第一段 by xiaolong xing

“在这个年代,大家有一种神圣化面向对象技术的倾向,很多人都把对象技术奉为高深的思想和理论。”

对这句话深不认同!把一种思考问题的方法神圣化本省就是幼稚的表现。

这句是啥意思? by lei zhao

其他主流语言如Java和C#,类型系统的已经被限制在一个相对简单的范畴内,说完败也不为过。
这句是啥意思?

不要陷入技术细节 by Yang Bob

IT是个最乐于扯皮的行业,如果这么研究下去,任何语言都会死,每个方法有他的应用范围和适用场景,难道我们在写代码的时候真的会去思考这么多理论问题吗?就算你思考的非常清楚了,你真的能做出好东西吗?

Re: 这句是啥意思? by Yang Bob

这句话太主观了,完败在那?

Re: 这篇真的很精彩,尤其是第一段 by 孔 明佑

看完之后觉得面向函数式很神圣

Re: 面向对象贴合实际 by 孔 明佑

什么书啊
我也想看

非常精彩的文章 by F Mic

作者道出了面向对象和函数式语言的核心思想,面向对象和函数数语言都是为了封装和模块化。实际上函数式语言的模块化比面向对象更好,函数式语言会强制将小的操作封装成函数,而面向对象的模块化取决与设计人员的能力。

正如作者所说,早期的函数式语言缺乏类型的限制,将来的发展将是静态类型的函数式语言。

用屁股思考 by Peng Sunny

linux和windows的区别:前者你在用计算机语言(命令行)和它交互;后者是计算机在用你的语言(窗口)和你交互。微软赚的是近人化的钱,而对于软件开发者什么时候用近人化语言开发?

可惜这篇文章还在吹捧计算机鸟语,让人去学鸟语,还将OO硬是纳入计算机技术范围来考量,说明作者根本不懂OO,跟随老外潮流吹呗。

欢迎探讨:
www.jdon.com/jivejdon/thread/41596

你说对了...面向对象的确不是一种计算模型 by Liu liu

软件系统的任务不只限于计算. 计算, 过程, 逻辑, 都只是现实世界中的一部分语义, 它们并不是全部的语义. 但对象基本上可以表达大部分的语义. 原因是它的语义层次比上述几种都要更低. 在哲学上它是唯物主义认识论的基础.
面向对象将软件系统的任务从计算退回到表达, 然后希望从这里开始构建全部的语义. 是一种去伪存真的过程.
所以, 如果此文是在讨论计算, 那么函数式更好. 如果是软件开发, 那么对象更好. 如果是语言, 则目前的语言都是垃圾....顺便支持一把楼上!

选择面向对象的原因 by 王 钟凯

大多数时候,我选择使用面向对象是因为,它的实现方式和人的思考方式接近,你甚至可以对不会编程的人介绍你的代码结构,因为它们非常清晰易懂。并没有考虑计算模型,硬件和其他。
清晰的思路对编程至关重要,有时如果要兼顾对象带来的思路,又想避免其效率低下的部分,也可以选择仅使用 “面向对象” 来 “分析问题”。

方便接口是什么意思? by pengfei cui

conventional interface吗?这个词是这么翻译吗?

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

28 讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT