Loading AI tools
来自维基百科,自由的百科全书
Common Lisp对象系统(CLOS)是一种面向对象编程设施,它是ANSI Common Lisp的一部分。CLOS是强力的动态对象系统,在根本上不同于静态语言,比如C++或Java中的那种OOP设施。CLOS受到更早期的Lisp对象系统,比如MIT Flavors和Xerox CommonLoops的启发,然而它比二者要更加泛化。
CLOS最初被提议作为一种附加件,首次出现在1988年发表的《Common Lisp面向对象编程》[3],和1990年出版的《Common Lisp语言》第二版(也叫做CLtL2)中。在1994年,CLOS被接受为ANSI标准Common Lisp的一部分,并且已经被适配入其他Lisp方言比如EuLisp和Emacs Lisp之中[4]。
CLOS的基本建造块是类、方法、泛化函数和类的实例。CLOS提供了定义它们的宏:defclass
、defmethod
和defgeneric
。实例通过方法make-instance
来创建。CLOS不是基于原型语言,类必须声明于对象可以实例化为这个类的成员之前。
类是槽的一个列表,它可以有多个超类,和一个特定的元类。槽(C++/Java用语中的成员变量),可以由类(一个类的所有实例共享这个槽)或实例来分配。每个槽都有一个名字,而一个槽的值可以使用函数slot-value
,通过这个名字来访问。可以定义额外的特定泛化函数,来写或读这些槽的值。在CLOS类中的每个槽,都必须有一个唯一的名字。
如同多数动态语言中的OO系统那样,CLOS不强制封装。任何槽都可以使用slot-value
函数,或通过(可选的自动生成的)访问子方法来访问。要通过slot-value
进行访问,就要知道这个槽的名字。CL编程者使用这个语言的包设施,来声明哪些函数或数据结构意图被导出。
CLOS允许多重继承。在多重继承中方法执行的缺省次序不正确的时候,编程者可以通过指定方法组合的次序,来解决这种菱形继承问题(类D同时继承一个超类A的两个子类B和C,四者形成菱形,如果A的某一方法被B和C覆写,D该继承二者中哪一个?)。在CLOS中已经解决了圆–椭圆问题(圆是椭圆的长轴与短轴相等的特殊情况,故而是其子类,但圆不能继承椭圆的使得其不再为圆的方法,比如单独改变长轴或短轴)。而多数OOP设计模式,要么消失,要么在性质上更加简单了[5]。
CLOS是动态的,意味着不只是内容,它的对象的“结构”,也可以在运行时间修改。CLOS支持现场(on-the-fly)变更类定义,即使正在考虑的这个类的实例已经存在;还有通过change-class
算子,变更一个给定实例的类成员关系。CLOS还允许在运行时间增加、重新定义和移除方法。
CLOS是一个多分派系统,这意味着方法,可以依据它们所要求的任何或所有实际参数来指定。多数OO语言是单分派的,意味着方法只能依据第一个实际参数来指定。其他不常见特征是方法不“属于”类;类不为泛化函数或方法提供命名空间。方法是独立于类而定义的,并且它们对类的槽没有特殊的访问(比如this
、self
或protected
)。
在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
在CLOS中的分派还不同于多数OO语言:
这个分派机制工作在运行时间。增加或移除方法,将导致在运行时间变更有效方法(即使在以相同实际参数调用泛化函数的时候)。变更方法组合还导致不同的有效方法。
除了常规(“主要”)方法之外,还有:before
、:after
和:around
这些“辅助”方法。前两个方法按照基于类层级的特定次序,分别先于或后于主要方法而调用。:around
方法可能控制主要方法是否执行。此外,编程者可以指定沿着类层级的所有可能的主要方法,是都应当被调用,还是只调用提供最接近匹配的那个方法。
“标准方法组合”提供上述的主要、之前、之后和围绕方法。还有具有其他方法类型的其他方法组合。可以定义新的(简单和复杂二者)方法组合和方法类型。
在ANSI Common Lisp标准之外,有一个广泛实现的对CLOS的扩展,叫做元对象协议(MOP)。MOP定义到CLOS实现基础支撑的标准接口,将类、槽描述、泛化函数和方法自身,当作元类的实例,并允许定义新的元类,和修改所有CLOS行为。CLOS MOP的灵活性,预示了面向方面编程,它是由同一群工程师比如Gregor Kiczales开发的。MOP通过一组协议,定义了整个对象系统的行为。这些定义是依据CLOS定义的。因此有可能通过扩展或变更已提供的CLOS系统的功能,来建立新的对象系统。1991年出版的图书《元对象协议的艺术》描述了CLOS MOP的使用和实现[6]。
各种Common Lisp实现对元对象协议有稍微不同的支持。Closer计划致力于提供缺失的特征[7]。
Flavor[8](和它的后继者New Flavors),是MIT Lisp机器上的对象系统。Lisp机器操作系统的很大部分和它的很多应用,使用了Flavors或New Flavors。Flavors介入了多重继承和mixin和其他一些特征[9]。Flavors几乎废止了,尽管存在针对Common Lisp实现。Flavors使用消息传递范型。New Flavors介入了泛化函数。
CommonLoops[10],是Xerox Interlisp-D的LOOPS的后继者。CommonLoops是针对Common Lisp的实现。叫做可移植CommonLoops(PCL)的可移植实现,是第一个CLOS实现。PCL被广泛的移植了,并仍为很多Common Lisp实现提供CLOS实现的基础。PCL绝大部分以可移植的Common Lisp实现,而只有很少的系统依赖部分。
由于CLOS的能力和表达力,还有历史上能获得到TinyCLOS,它是Gregor Kiczales为用于Scheme而书写的简化的可移植的CLOS实现,故而类CLOS的基于MOP的对象系统,成为大多数Lisp方言实现的事实规范,还能在一些其他语言的OOP设施中找到它:
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.