Common Lisp对象系统CLOS)是一种面向对象编程设施,它是ANSI Common Lisp的一部份。CLOS是强力的动态对象系统,在根本上不同于静态语言,比如C++Java中的那种OOP设施。CLOS受到更早期的Lisp对象系统,比如MIT Flavors英语Flavors (programming language)Xerox CommonLoops英语CommonLoops的启发,然而它比二者要更加泛化。

Quick Facts 编程范型, 实作者 ...
Common Lisp对象系统
编程范型面向对象
实作者ANSI X3J13英语X3J13委员会
发行时间1988年,​36年前​(1988
实作语言Common Lisp
操作系统跨平台
网站Common Lisp HyperSpec, Chapter 7: Objects
启发语言
MIT Flavors英语Flavors (programming language), Xerox CommonLoops英语CommonLoops
影响语言
Dylan, Tiny CLOS[1], COOPS[2], STklos英语STklos, Gauche英语Gauche (Scheme implementation)
Close

历史

CLOS最初被提议作为一种附加件,首次出现在1988年发表的《Common Lisp面向对象编程英语Object-Oriented Programming in Common Lisp[3],和1990年出版的《Common Lisp语言英语Common Lisp the Language》第二版(也叫做CLtL2)中。在1994年,CLOS被接受为ANSI标准Common Lisp的一部份,并且已经被适配入其他Lisp方言比如EuLisp英语EuLispEmacs Lisp之中[4]

特征

CLOS的基本建造块是方法泛化函数和类的实例。CLOS提供了定义它们的宏:defclassdefmethoddefgeneric。实例通过方法make-instance来创建。CLOS不是基于原型语言,类必须声明于对象可以实例化为这个类的成员之前。

类是槽的一个列表,它可以有多个超类,和一个特定的元类。槽(C++/Java用语中的成员变量),可以由类(一个类的所有实例共享这个槽)或实例来分配。每个槽都有一个名字,而一个槽的值可以使用函数slot-value,通过这个名字来访问。可以定义额外的特定泛化函数,来写或读这些槽的值。在CLOS类中的每个槽,都必须有一个唯一的名字。

如同多数动态语言中的OO系统那样,CLOS不强制封装。任何槽都可以使用slot-value函数,或通过(可选的自动生成的)访问子方法来访问。要通过slot-value进行访问,就要知道这个槽的名字。CL编程者使用这个语言的设施,来声明哪些函数或数据结构意图被导出。

CLOS允许多重继承。在多重继承中方法执行的缺省次序不正确的时候,编程者可以通过指定方法组合的次序,来解决这种菱形继承问题英语diamond problem(类D同时继承一个超类A的两个子类B和C,四者形成菱形,如果A的某一方法被B和C覆写,D该继承二者中哪一个?)。在CLOS中已经解决了圆–椭圆问题英语Circle-ellipse problem(圆是椭圆的长轴与短轴相等的特殊情况,故而是其子类,但圆不能继承椭圆的使得其不再为圆的方法,比如单独改变长轴或短轴)。而多数OOP设计模式,要么消失,要么在性质上更加简单了[5]

CLOS是动态的,意味着不只是内容,它的对象的“结构”,也可以在运行时间修改。CLOS支持现场(on-the-fly)变更类定义,即使正在考虑的这个类的实例已经存在;还有通过change-class算子,变更一个给定实例的类成员关系。CLOS还允许在运行时间增加、重新定义和移除方法。

多分派

CLOS是一个多分派系统,这意味着方法,可以依据它们所要求的任何或所有实际参数来指定。多数OO语言是单分派的,意味着方法只能依据第一个实际参数来指定。其他不常见特征是方法不“属于”类;类不为泛化函数或方法提供命名空间。方法是独立于类而定义的,并且它们对类的槽没有特殊的访问(比如thisselfprotected)。

在CLOS中的方法被组织入泛化函数。泛化函数是像函数一样可调用的一个对象,它关联着有共享的名字和参数结构的方法的一个搜集,其中每个都特定于不同的实际参数。因为Common Lisp为结构和内建数据类型(数值、字符串、字符、符号等)提供了非CLOS类,CLOS分派也工作在这些非CLOS类之上。CLOS还支持分派在个体对象之上(eql特殊化者)。CLOS缺省的不支持分派于所有Common Lisp数据类型上,比如分派不工作于完全特殊化的数组类型,或通过deftype介入的类型。然而多数Common Lisp实现,提供了元对象协议,它允许泛化函数提供特定于应用的特殊化和分派规则。例如采用SBCL演示的下述代码:

; 声明共同的实际参数结构原型
* (defgeneric f (x y))
#<STANDARD-GENERIC-FUNCTION COMMON-LISP-USER::F (0)>

; 定义对(f integer t)的实现,这里的t匹配所有类型
* (defmethod f ((x integer) y) 1)
#<STANDARD-METHOD COMMON-LISP-USER::F (INTEGER T) {10018B1F03}>

* (f 1 2.0)
1

; 定义对(f integer real)的实现
* (defmethod f ((x integer) (y real)) 2)
#<STANDARD-METHOD COMMON-LISP-USER::F (INTEGER REAL) {1001A0BB83}>

* (f 1 2.0) ; 分派发生在运行时间
2

方法组合

Thumb
在ANSI common lisp中的标准方法组合,这里色泽最浅的是类A,类B是类A的子类,色泽最深的类C是类B的子类。合成有效方法的各个方法的执行次序是:
①类A的:around方法、类B的:around方法、类C的:around方法,
②类A的:before方法、类B的:before方法、类C的:before方法,
③类A的主要方法、类B的主要方法、类C的主要方法,
④类C的:after方法、类B的:after方法、类A的:after方法。

在CLOS中的分派还不同于多数OO语言:

  1. 给出一个实际参数列表,确定一个可应用的方法的列表。
  2. 这个列表依据它们的形式参数的特殊化者(specializer)的特殊性(specificity)来排序。
  3. 接着使用泛化函数所用的那个方法组合,将从这个列表选择出的那些方法组合成一个有效方法。
  4. 接着用最初的实际参数调用这个有效方法。

这个分派机制工作在运行时间。增加或移除方法,将导致在运行时间变更有效方法(即使在以相同实际参数调用泛化函数的时候)。变更方法组合还导致不同的有效方法。

除了常规(“主要”)方法之外,还有:before:after:around这些“辅助”方法。前两个方法按照基于类层级的特定次序,分别先于或后于主要方法而调用。:around方法可能控制主要方法是否执行。此外,编程者可以指定沿着类层级的所有可能的主要方法,是都应当被调用,还是只调用提供最接近匹配的那个方法。

“标准方法组合”提供上述的主要、之前、之后和围绕方法。还有具有其他方法类型的其他方法组合。可以定义新的(简单和复杂二者)方法组合和方法类型。

元对象协议

在ANSI Common Lisp标准之外,有一个广泛实现的对CLOS的扩展,叫做元对象协议(MOP)。MOP定义到CLOS实现基础支撑的标准接口,将类、槽描述、泛化函数和方法自身,当作元类的实例,并允许定义新的元类,和修改所有CLOS行为。CLOS MOP的灵活性,预示了面向方面编程,它是由同一群工程师比如Gregor Kiczales英语Gregor Kiczales开发的。MOP通过一组协议,定义了整个对象系统的行为。这些定义是依据CLOS定义的。因此有可能通过扩展或变更已提供的CLOS系统的功能,来建立新的对象系统。1991年出版的图书《元对象协议的艺术英语The Art of the Metaobject Protocol》描述了CLOS MOP的使用和实现[6]

各种Common Lisp实现对元对象协议有稍微不同的支持。Closer计划致力于提供缺失的特征[7]

来自更早的基于Lisp的对象系统的影响

Flavor英语Flavors (programming language)[8](和它的后继者New Flavors),是MIT Lisp机器上的对象系统。Lisp机器操作系统的很大部份和它的很多应用,使用了Flavors或New Flavors。Flavors介入了多重继承mixin和其他一些特征[9]。Flavors几乎废止了,尽管存在针对Common Lisp实现。Flavors使用消息传递范型。New Flavors介入了泛化函数

CommonLoops英语CommonLoops[10],是Xerox Interlisp英语Interlisp-D的LOOPS的后继者。CommonLoops是针对Common Lisp的实现。叫做可移植CommonLoops(PCL)的可移植实现,是第一个CLOS实现。PCL被广泛的移植了,并仍为很多Common Lisp实现提供CLOS实现的基础。PCL绝大部份以可移植的Common Lisp实现,而只有很少的系统依赖部份。

其他语言中的CLOS

由于CLOS的能力和表达力,还有历史上能获得到TinyCLOS,它是Gregor Kiczales英语Gregor Kiczales为用于Scheme而书写的简化的可移植的CLOS实现,故而类CLOS的基于MOP的对象系统,成为大多数Lisp方言实现的事实规范,还能在一些其他语言的OOP设施中找到它:

引用

延伸阅读

参考文献

Wikiwand in your browser!

Seamless Wikipedia browsing. On steroids.

Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.

Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.