跳到主要内容

Alan Kay 谈「面向对象编程」的本意

下面是 Alan Kay 于 2003 年 7 月回复 Stefan Ram 的两封邮件。Kay 是「面向对象」这一术语的提出者,他在信中澄清了这个概念最初的含义。

来源: http://www.purl.org/stefan_ram/pub/doc_kay_oop_en


邮件一 —— 2003 年 7 月 23 日

主题: Re: Clarification of "object-oriented"

Hi Stefan —

抱歉回复晚了,我之前在度假。

(以下是 Stefan Ram 在 7 月 17 日来信中的问题,此处略作整理。)

我想为我的教程页面找到一个关于「面向对象编程」的权威说法。我认为只有两个来源足够权威:一个是国际标准化组织在 ISO/IEC 2382-15 中的定义,另一个就是您,因为据说这个术语是您创造的。

我非常确定是我。

您能解释一下「面向对象」这个术语最早是在何时、何地使用的吗?

那是 1966 年 11 月之后在犹他大学。当时受 Sketchpad、Simula、ARPAnet 的设计、Burroughs B5000,以及我自己的生物学和数学背景的影响,我构思了一种编程架构。大概是在 1967 年,有人问我正在做什么,我说:「这是面向对象编程」。

它最初的概念包含以下几个部分:

  • 我把对象想象成生物细胞,或者是网络中独立的计算机,它们只能通过消息互相通信(所以消息传递从一开始就是核心——只是过了一段时间,我才找到在编程语言中足够高效地实现消息传递的方法)。

  • 我想消除「数据」这个概念。B5000 通过其几乎不可思议的硬件架构几乎做到了这一点。我意识到,细胞 / 整台计算机的隐喻可以消除数据,<- 也可以只是另一种消息符号(我花了很长时间才想清楚这一点,因为我过去真的把这些符号都当成函数和过程的名字)。

  • 我的数学背景让我意识到,每个对象都可以关联多个代数,而且可以存在这些代数的家族,这非常有用。「多态」这个词是后来才被强加进来的(我想是 Peter Wegner),它并不太准确,因为它来自函数命名法,而我想要的远不止函数。我自己造了一个词「genericity」,用来以准代数形式处理泛化行为。

  • 我不喜欢 Simula I 或 Simula 67 实现继承的方式(尽管我认为 Nygaard 和 Dahl 是非常杰出的思想家和设计师)。所以我决定在更好地理解继承之前,不把它作为内置特性。

我最初对这种架构的实验,使用了一种从 van Wijngaarten 和 Wirth 的「Generalization of Algol」以及 Wirth 的 Euler 改编而来的模型。这两者都有点像 LISP,但语法更传统、可读性更好。当时我还不理解 LISP 那种具体的元语言思想,但已经通过不同来源获得了一些关于可扩展语言的想法,包括 Irons 的 IMP。

第二阶段是我终于理解了 LISP,并用这种理解去构建更优雅、更小、更强大、更加延迟绑定的底层结构。Dave Fisher 的论文是用「McCarthy 风格」写的,他关于可扩展控制结构的想法非常有帮助。另一个重大影响来自 Carl Hewitt 的 PLANNER(它从未得到应有的认可,尽管它很早就预见到了 Prolog)。

后来的 Xerox PARC Smalltalk 就是基于以上工作诞生的。后续的各种 Smalltalk,正如我在《Smalltalk 历史》一章末尾所批评的,它们退步回了 Simula 的方向,而且没有把扩展机制替换成同样有用但更安全的形式。

对您来说,「面向对象编程」意味着什么?

(我并不反对类型,但我不知道哪种类型系统不是彻头彻尾的麻烦,所以我仍然喜欢动态类型。)

对我来说,面向对象只意味着:消息传递、状态与过程的局部保留、保护及隐藏,以及所有东西的极端延迟绑定。 它可以在 Smalltalk 和 LISP 中实现。可能还有其他系统也能做到,但我不知道。

Cheers,

Alan


邮件二 —— 2003 年 7 月 26 日

主题: Re: Clarification of "object-oriented"

有一件事我之前应该提到:Simula 催化了两个主要方向。较早的一个(纯属偶然)是我走的生物/网络、非数据-过程路线。另一个稍晚成为研究对象的是抽象数据类型(ADT),它得到了更多的关注。

回顾整个历史,我们看到原初的面向对象思想是从 ADT 开始的,然后分出一个小岔路,通向我所称的「对象」——这条岔路最终导致了 Smalltalk 等。但在这个小岔路之后,计算机科学的主流基本上还是走了 ADT 路线,并希望继续留在数据-过程范式里。

历史上值得注意的工作包括:

  • Burroughs 220 文件系统(美国空军项目,我在 Smalltalk 历史中描述过);
  • Doug Ross 在 MIT 的早期工作(AED 及更早),他主张在数据结构中嵌入过程指针;
  • Sketchpad,它具有完整的多态——例如,数据结构中的同一个偏移量意味着「显示」,而指针会指向该结构所代表对象类型的适当例程;
  • Burroughs B5000,它的程序引用表是真正的「大对象」,包含指向「数据」和「过程」的指针,当硬件试图获取数据却发现是指向过程的指针时,往往能正确处理。

我最初在犹他做的实验,解决的问题就是只用方法和对象来「让数据消失」。60 年代末(我想)Bob Balzer 写了一篇很精彩的论文叫《Dataless Programming》;不久之后 John Reynolds 写了一篇同样精彩的论文《Gedanken》(我想是 1970 年),他展示了以正确方式使用 lambda 表达式可以让数据被过程抽象。

喜欢「对象作为非数据」的人比较少,包括我自己、Carl Hewitt、Dave Reed 和其他一些人——这群人几乎都来自 ARPA 社区,并以某种方式参与了 ARPAnet → Internet 的设计,其中计算的基本单元就是一台完整的计算机。但这也说明一个想法可以多顽固地坚持下来:在整个七十年代和八十年代,有很多人都试图用「远程过程调用」(RPC)来凑合,而不是去思考对象和消息。Sic transit gloria mundi(世间荣耀,转瞬即逝).

Cheers,

Alan

2003 年 7 月 26 日晚上 10:05,Stefan Ram 写道:

2003 年 7 月 23 日上午 9:33,Alan Kay 写道:

对我来说,面向对象只意味着:消息传递、状态与过程的局部保留、保护及隐藏,以及所有东西的极端延迟绑定。

Hi Alan,

我想对您的解释(包括上面没有引用的部分)说声"谢谢"!

"local retention" 在面向对象的语境中对我来说是一个新概念。我想它指的是 state-process,意思是一个对象拥有它自己的 state-process,对象的状态就保留在对象自身内部。

我已经把您的回复发布到网上了,但为了保护隐私,我移除了电子邮件地址和类似的邮件头信息。

再次感谢,

Stefan


要点

对 Alan Kay 来说,面向对象编程从来都不是主要关于继承、类或封装本身。它关乎的是:

  • 消息传递:独立实体之间的通信
  • 状态与过程的局部保留、保护及隐藏:对象自己持有并保护自己的状态
  • 极端延迟绑定:一切都尽可能晚地绑定

这也是 面向因果 认为传统「对象即数据容器」模型有局限的原因之一:当对象被简化为带方法的状态集合时,消息传递、自主计算单元这些更深层的思想就丢失了——随之而来的是,用清晰的方式建模随时间展开的因果关系也变得更加困难。