커먼 리스프 오브젝트 시스템

thumb|right|ANSI 커먼 리스프의 표준 메서드 조합
커먼 리스프 오브젝트 시스템(Common Lisp Object System, CLOS)은 ANSI 커먼 리스프에서 객체 지향 프로그래밍을 위한 기능이다. CLOS는 C++나 자바와 같은 더 정적 언어에서 발견되는 객체 지향 프로그래밍 기능과는 근본적으로 다른 동적 객체 시스템이다. CLOS는 MIT Flavors 및 CommonLoops와 같은 이전 리스프 객체 시스템에서 영감을 얻었지만, 둘보다 더 일반적이다. 원래는 추가 기능으로 제안되었으나, CLOS는 커먼 리스프의 ANSI 표준의 일부로 채택되었으며 EuLisp 또는 이맥스 리스프와 같은 다른 리스프 방언에도 적용되었다.

 기능 
CLOS의 기본 구성 요소는 메서드, 클래스, 해당 클래스의 인스턴스, 그리고 제네릭 함수이다.
CLOS는 이들을 정의하기 위한 매크로를 제공한다: defclass, defmethod, defgeneric. 인스턴스는 make-instance 메서드를 사용하여 생성된다.

클래스는 여러 수퍼클래스, 슬롯 목록(C++/자바 용어로는 멤버 변수), 그리고 특수한 메타클래스를 가질 수 있다. 슬롯은 클래스별(클래스의 모든 인스턴스가 슬롯을 공유) 또는 인스턴스별로 할당될 수 있다. 각 슬롯은 이름을 가지며, 슬롯의 값은 slot-value 함수를 사용하여 그 이름으로 접근할 수 있다. 또한 슬롯의 값을 쓰거나 읽기 위해 특별한 제네릭 함수를 정의할 수 있다. CLOS 클래스의 각 슬롯은 고유한 이름을 가져야 한다.

CLOS는 다중 디스패치 시스템이다. 이는 메서드가 필수 인자 중 일부 또는 전부에 따라 특수화될 수 있음을 의미한다. 대부분의 객체 지향 언어는 단일 디스패치이며, 이는 메서드가 첫 번째 인자에 대해서만 특수화됨을 의미한다. 또 다른 특이한 기능은 메서드가 클래스에 "속하지" 않는다는 것이다. 클래스는 제네릭 함수나 메서드에 대한 이름공간을 제공하지 않는다. 메서드는 클래스와 독립적으로 정의되며, 클래스 슬롯에 대한 특별한 접근(예: "this", "self" 또는 "protected") 권한이 없다.

CLOS의 메서드는 제네릭 함수로 그룹화된다. 제네릭 함수는 함수처럼 호출할 수 있는 객체이며, 공유된 이름과 인자 구조를 가진 메서드 컬렉션을 연결하고, 각 메서드는 다른 인자에 대해 특수화된다. 커먼 리스프는 구조체 및 내장 데이터 유형(숫자, 문자열, 문자, 심볼 등)에 대해 비CLOS 클래스를 제공하므로, CLOS 디스패치는 이러한 비CLOS 클래스에서도 작동한다. CLOS는 개별 객체(eql 특수화자)에 대한 디스패치도 지원한다. CLOS는 기본적으로 모든 커먼 리스프 데이터 유형에 대한 디스패치를 지원하지 않는다(예를 들어, 완전히 특수화된 배열 유형이나 deftype에 의해 도입된 유형에는 디스패치가 작동하지 않는다). 그러나 대부분의 커먼 리스프 구현은 제네릭 함수가 응용 프로그램별 특수화 및 디스패치 규칙을 제공할 수 있도록 하는 메타객체 프로토콜을 제공한다.

CLOS의 디스패치도 대부분의 객체 지향 언어와 다르다:

 인자 목록이 주어지면, 적용 가능한 메서드 목록이 결정된다.
 이 목록은 매개변수 특수화자의 특수성에 따라 정렬된다.
 이 목록에서 선택된 메서드는 제네릭 함수가 사용하는 메서드 조합을 사용하여 유효 메서드로 결합된다.
 유효 메서드는 원래 인자와 함께 호출된다.

이 디스패치 메커니즘은 런타임에 작동한다. 따라서 메서드를 추가하거나 제거하면 런타임에 유효 메서드가 변경될 수 있다(제네릭 함수가 동일한 인자로 호출되더라도). 메서드 조합을 변경하는 것도 다른 유효 메서드로 이어질 수 있다.

예를 들어:
; 공통 인자 구조 프로토타입을 선언합니다.
(defgeneric f (x y))

; (f integer y)에 대한 구현을 정의합니다. 여기서 y는 모든 유형과 일치합니다.
(defmethod f ((x integer) y)
 1)

(f 1 2.0) => 1

; (f integer real)에 대한 구현을 정의합니다.
(defmethod f ((x integer) (y real))
 2)

(f 1 2.0) => 2 ; 런타임에 디스패치가 변경되었습니다.

대부분의 동적 언어의 객체 지향 시스템과 마찬가지로, CLOS는 캡슐화를 강제하지 않는다. 어떤 슬롯이든 slot-value 함수를 사용하거나 (선택적으로 자동 생성되는) 접근자 메서드를 통해 접근할 수 있다. slot-value를 통해 접근하려면 슬롯의 이름을 알아야 한다. CL 프로그래머는 언어의 패키지 기능을 사용하여 어떤 함수나 데이터 구조를 내보낼지 선언한다.

일반("기본") 메서드 외에도 :before, :after, :around "보조" 메서드가 있다. 앞의 두 가지는 클래스 계층 구조에 따라 특정 순서로 기본 메서드 이전에 또는 이후에 호출된다. :around 메서드는 기본 메서드가 전혀 실행될지 여부를 제어할 수 있다. 또한 프로그래머는 클래스 계층 구조를 따라 가능한 모든 기본 메서드가 호출되어야 하는지 아니면 가장 일치하는 메서드만 호출되어야 하는지 지정할 수 있다.

표준 메서드 조합은 위에서 설명한 기본, 비포어, 애프터, 어라운드 메서드를 제공한다. 다른 메서드 유형을 가진 다른 메서드 조합도 있다. 새로운 (단순 및 복잡한) 메서드 조합과 메서드 유형을 정의할 수 있다.

CLOS는 다중 상속을 허용한다. 다중 상속에서 메서드가 실행되는 기본 순서가 올바르지 않을 경우, 프로그래머는 메서드 조합의 순서를 지정하여 다이아몬드 상속 문제를 해결할 수 있다.

CLOS는 동적이다. 즉, 객체의 내용뿐만 아니라 구조도 런타임에 수정할 수 있다. CLOS는 클래스 정의를 실시간으로 변경하는 것(해당 클래스의 인스턴스가 이미 존재하는 경우에도)은 물론, change-class 연산자를 통해 주어진 인스턴스의 클래스 멤버십을 변경하는 것을 지원한다. CLOS는 런타임에 메서드를 추가, 재정의 및 제거하는 것도 허용한다. 원-타원 문제는 CLOS에서 쉽게 해결되며, 대부분의 객체 지향 디자인 패턴은 사라지거나 질적으로 단순해진다.

CLOS는 프로토타입 언어가 아니다: 객체가 해당 클래스의 멤버로 인스턴스화되기 전에 클래스를 정의해야 한다.

 메타객체 프로토콜 
ANSI 커먼 리스프 표준 외부에, 메타객체 프로토콜(MOP)이라는 CLOS에 대한 널리 구현된 확장이 있다. MOP는 CLOS 구현의 기반에 대한 표준 인터페이스를 정의하며, 클래스, 슬롯 설명, 제네릭 함수 및 메서드 자체를 메타클래스의 인스턴스로 취급하고, 새로운 메타클래스 정의 및 모든 CLOS 동작 수정을 허용한다. CLOS MOP의 유연성은 나중에 그레고르 키찰레스와 같은 동일한 엔지니어 중 일부가 개발한 관점 지향 프로그래밍을 예견한다. MOP는 일련의 프로토콜에 의해 전체 객체 시스템의 동작을 정의한다. 이들은 CLOS의 관점에서 정의된다. 따라서 제공된 CLOS 기능을 확장하거나 변경함으로써 새로운 객체 시스템을 생성하는 것이 가능하다. 책 The Art of the Metaobject Protocol은 CLOS MOP의 사용 및 구현을 설명한다.

다양한 커먼 리스프 구현은 메타객체 프로토콜에 대해 약간 다른 지원을 제공한다. Closer 프로젝트는 누락된 기능을 제공하는 것을 목표로 한다.

 이전 리스프 기반 객체 시스템의 영향 
Flavors (및 그 후속작 New Flavors)는 MIT 리스프 머신의 객체 시스템이었다. 리스프 머신 운영체제의 대부분과 그를 위한 많은 응용 프로그램은 Flavors 또는 New Flavors를 사용한다. Flavors는 다중 상속과 믹스인 등의 기능을 도입했다. Flavors는 대부분 구식이 되었지만, 커먼 리스프용 구현은 존재한다. Flavors는 메시지 전달 패러다임을 사용했다. New Flavors는 제네릭 함수를 도입했다.

CommonLoops는 제록스 Interlisp-D의 LOOPS의 후속작이었다. CommonLoops는 커먼 리스프를 위해 구현되었다. Portable CommonLoops(PCL)라는 이식 가능한 구현이 CLOS의 첫 번째 구현이었다. PCL은 널리 이식되었으며 여전히 여러 커먼 리스프 구현의 CLOS 구현의 기반을 제공한다. PCL은 시스템 의존적인 몇 가지 부분만을 제외하고 대부분 이식 가능한 커먼 리스프로 구현된다.

 다른 프로그래밍 언어의 CLOS 
CLOS의 강력함과 표현력, 그리고 Tiny CLOS(그레고르 키찰레스가 Scheme에서 사용하기 위해 작성한 단순화된 이식 가능한 CLOS 구현)의 역사적 가용성으로 인해, CLOS와 유사한 MOP 기반 객체 시스템은 대부분의 리스프 방언 구현에서 사실상의 표준이 되었으며, 일부 다른 언어의 OOP 기능에도 적용되었다:

 COS, C 객체 시스템
 딜런
 Dynace, C로 구현된 (대부분) CLOS 구현체
 이맥스 리스프용 EIEIO
 Gauche, CLOS가 있는 스킴
 GNU 가일의 GOOPS
 ISLISP의 ILOS
 Meroon, 스킴의 객체 시스템
 Sagittarius, CLOS가 있는 스킴
 ScmObj, 스킴용
 MIT 스킴용 SOS
 STklos, CLOS가 있는 스킴
 래킷의 Swindle
 치킨 스킴의 COOPS
 Skill용 VCLOS
 Tiny CLOS
 S 및 R 프로그래밍 언어의 S4 클래스

 더 읽어보기 
 
 

 각주 

 참고 문헌 
 Sonya Keene, Object-Oriented Programming in Common Lisp: A Programmer's Guide to CLOS, 1988, Addison-Wesley. 
 Gregor Kiczales, Jim des Rivieres, and Daniel G. Bobrow, The Art of the Metaobject Protocol, 1991, MIT Press. 
 Jo A. Lawless and Molly M. Miller, Understanding CLOS: the Common Lisp Object System, 1991, Digital Press, 
 Andreas Paepcke, Object-Oriented Programming: the CLOS Perspective, 1993, The MIT Press. 
 The Common Lisp Object System: An Overview by Richard P. Gabriel and Linda DeMichiel provides a good introduction to the motivation for defining classes by means of generic functions.
 Fundamentals of CLOS by Nick Levine provides a step-by-step exposure to the implementation of OO concepts in CLOS, and how to utilize them. It is intended for anybody with a basic knowledge of Lisp or Scheme.
 Common Lisp HyperSpec, Chapter 7: Objects

분류:객체 지향 프로그래밍
분류:리스프