基于原型编程

基于原型编程英語:)或称为原型编程,是面向对象编程的子系统和一种方式。在原型编程中,行为重用(在基于类的语言通常称为继承)是通过复制已经存在的原型对象的过程实现的。这个模型一般被认为是无类的、面向原型、或者是基于实例的编程。

原型编程最初的(也是最经典的)例子是编程语言Self,它是由David Ungar和Randall Smith开发的。但是无类编程方式最近变得越来越受欢迎,并且被JavaScriptCecilNewtonScriptIoREBOL,还有一些其他的程序语言所采纳。

与基于类编程的比较

基于类编程当中,对象总共有两种类型。定义了对象的基本布局和函数特性,而接口是“可以使用的”对象,它基于特定类的样式。在此模型中,类表现为行为和结构的集合,就接口持有对象的数据而言,对所有接口来说是相同的。区分规则因而首先是基于结构和行为,而后是状态。

原型编程的主张者经常争论说基于类的语言提倡使用一个关注分类和类之间关系开发模型。与此相对,原型编程看起来提倡程序员关注一系列对象实例的行为,而之后才关心如何将这些对象划分到最近的使用方式相似的原型对象,而不是分成类。因为如此,很多基于原型的系统提倡运行时原型的修改,而只有极少数基于类的物件導向系统(比如第一个动态物件導向的系统Smalltalk)允许类在程序运行时被修改。

原型编程常与认知心理学的特定思想流派有关,同样强调“prototypes”和“exemplars”作为学习过程中的关键词。

考虑到绝大多数基于原型的系统是基于解释性的和动态类型程序语言,这里要重点指出的是静态类型语言实现基于原型从技术上是可行的。用基于原型编程描述的Omega 页面存档备份,存于语言[1],就是这样系统的一个例子。尽管根据Omega网站所述,Omega也不是完全的静态,但是可能的时候,它的编译器有时会使用静态绑定来改进程序的效率。

对象构造

在基于类的语言中,一个新的实例通过类构造器和构造器可选的参数来构造,结果实例由类选定的行为和布局建立模型。在基于原型的系统中构造对象有两种方法,通过复制(cloning)已有的对象,或者通过扩展空(nihilo)对象创建,因为大多数系统提供了不同的复制方法,扩展空对象的方式并不显著[2]

提供扩展空对象创建的系统允许对象从空白中创建而无需从已有的原型中复制。这样的系统提供特殊的文法用以指定新对象的行为和属性,无须参考已存在的对象。在很多原型语言中,通常有一个Object原型,其中有普遍需要的方法。它被用作所有其它对象的最终原型。扩展空对象创建可以保证新对象不会被顶级对象的命名空间污染。(在JavaScript中,可以利用null原型來做到,比如Object.create(null))。

复制指一个新对象通过复制一个已经存在的对象(就是他的原型)来构造自己的过程。于是新的对象拥有原来对象的所有属性,从这一点出发新对象的属性可以被修改。在某些系统中,子对象持有一个到它原型的直接链接(经由授权或类似方式)。并且原型的改变同样会导致它的副本的变化。其他系统中,如类Forth的程序语言Kevo,在此情况下不传播原型的改变,而遵循一个更加连续的模型,其中被复制的对象改变不会通过他的副本传播[3]

// JavaScript中真实的原型继承样式的例子。 

// 使用文字对象记号{}建立的“无中生有”对象。
var foo = {name: "foo", one: 1, two: 2};

// 另一个“无中生有”对象。
var bar = {three: 3};

// Gecko和Webkit JavaScript引擎可以直接的操纵内部的原型链接。
// 为了简单起见,我们假装下面几行代码可以工作而不考虑使用的引擎:
bar.__proto__ = foo; // foo现在是bar的原型。

// 如果我们尝试从bar访问foo的属性,从此以后会成功。 
bar.one // 解析为1。

// 子对象的属性也是可访问的。
bar.three // 解析为3。

// 自身的属性遮蔽原型属性。
bar.name = "bar";
foo.name; // 无影响,解析为"foo"。
bar.name; // 解析为"bar"。

下面是个在 JavaScript 1.8.5 以上版本的例子(参见 http://kangax.github.com/es5-compat-table/ 页面存档备份,存于

var foo = {one: 1, two: 2};

// 等价于上例的bar.[[ prototype ]] = foo
var bar = Object.create( foo );

bar.three = 3;

bar.one; // 1
bar.two; // 2
bar.three; // 3

委托

在使用委托的基于原型的语言中,运行时语言可以仅仅通过循着一个序列的指针直到找到匹配这样的方式来定位属性或者寻找寻找正确的数据。所有这些建立行为共享的行为需要的是委托指针。不像是基于类的物件導向语言中类和接口的关系,原型和他的分支之间的关系并不要求子对象有相似的内存结构,因为如此,子对象可以随着时间推移而继续被修改,而无须像基于类的系统那样整理结构。还有一个要提到的地方是,不仅仅是数据,方法也能被修改。因为这个原因,大多数基于原型的语言把数据和方法提作“槽”(slot)。

串接

在纯粹的原型,又被称作串接(concatenative)原型(以Kevo语言为例)中,没有到被复制的原型对象的指针或链接。原型对象以重新给定名字(或引用)的方式被确实的复制了。这个过程类似于生物学上的分裂,属性和方法被原样复制[4]

这样做的好处包括对象的作者可以修改这份副本而无须担心对此父类的其他子类产生副作用。进一步的优点是查找属性运算的消耗同授权相比大大降低了,授权查找必须遍历整个委托链才能判定不存在。

串接的坏处包括传播变化到整个系统的难度;如果一个变化作用到某个原型,它不会立即或者自动的对它的所有副本生效。然而Kevo提供了额外的在对象系统中传播变化的方式。这种方式是基于他们的相似性(所谓的family相似)[4],而非像委托模型具有代表性的那样源自分类学。

另外一个坏处是在这个模型的大多数自然的实现下,每一个副本上都有额外的内存被浪费掉了(相对委托模型而言),因为副本和原型之间有相同的部分存在。然而,在共享的实现和后台数据中提供串接行为的编程编程是可行的。这种做法为Kevo所遵从[5]

批评

那些经常批评基于原型系统而支持基于类的对象模型的人通常有类似静态类型系统相对于动态类型系统的担心。通常这些担心是:正确性、安全性、可预测性以及效率。

在前三点上,类可以看作和类型等效(多数静态语言遵守此规则)而且提供保证他们实例的契约,而对这些实例的使用者保证特定场景中的行为。

在最后一点上,效率,类的声明简化了编译器的组织,允许开发高效的方法以及实例变量查找。对Self语言来说,大多开发时间都消耗在开发、编译以及解释技术用以改进基于原型的系统相对于基于类的系统。举例来说Lisaac产生的代码速度几乎跟C一样快。测试是由MPEG-2编码器的Lisaac版本得出的,它由一个C语言版本复制而来。测试显示,Lisaac版本比C版本慢1.9%,但代码行数少了37%。然而C语言并非物件導向语言,而是一个过程式语言。Lisaac跟C++版本相比可能更说明问题。

最普遍的对基于原型的语言的批评来自不喜欢它的软件开发者社区,僅管有JavaScript的人气和市场。对基于原型系统的了解程度似乎因为JavaScript框架的廣泛應用以及JavaScript针对web 2.0的复杂应用而改变[6]。很可能由于这些原因,在ECMAScript标准的第四版开始尋求使JavaScript提供基于类的构造,且ECMAScript第六版有提供“類”作為原有的原型架構之上的語法糖,提供建構物件與處理繼承時的另一種語法[7]

支持基于原型编程的语言

引用

  1. Blaschek, Günther. . . : 177.
  2. Dony, Chistophe; Malenfan, Jacques; Bardou, Daniel. (PDF). . : 17 [2020-10-10]. (原始内容存档 (PDF)于2013-06-15).
  3. Taivalsaari, Antero. . . : 14.
  4. Antero Taivalsaar. . Tampere University of Technology. 2009 [2015-03-11]. (原始内容存档于2020-11-26) (英语). Kevo implemented a pure concatenation-based object model in which new objects were created by copying and the namespaces of all the objects were always fully self-contained. … Furthermore, Kevo had an internal clone family mechanism that made it possible to track the “genealogy” of changes among groups of objects, so that changes to individual objects could be propagated to other objects when necessary.
  5. Taivalsaari, Antero. . Technical Report Report LACIR 92-02 (University of Victoria). 1992.
  6. . A List Apart. 2016-04-26 [2018-10-21]. (原始内容存档于2021-01-03) (美国英语).
  7. . JavaScript reference. Mozilla Developer Network. [9 February 2016]. (原始内容存档于2021-01-14).

參考來源

  • James Noble (ed.), Antero Taivalsaari (ed.), Ivan Moore (ed.) (编). . Springer-Verlag. 1999. ISBN 981-4021-25-3.
  • Abadi, Martin; Luca Cardelli. . Springer-Verlag. ISBN 0-387-94775-2.

外部链接

This article is issued from Wikipedia. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.