SmalltalkObjectsandDesign:Chapter 20
- 제 20 장 메타클래스
메타클래스
여태까지 다형성, 패턴, 프레임워크, 그리고 나머지 일반적인 객체 지향의 주제들을 학습했다. 이번 장은 그러한 주요 주제들로부터 잠시 벗어나 스몰토크가 일관된 객체의 뷰를 유지하게 위해 얼마나 노력하는지 궁금한 독자들을 위해 최종 안내서를 제공한다. 그 동안 간단하면서도 복잡한 다음 질문에 답을 해본다: "new 메서드는 어디에 있는가?" 이 답을 이해하지 못한 채 수 년 간 스몰토크에서 유능히 프로그래밍하는 것도 가능하지만 답을 안다면 난해한 메타클래스의 세계를 발견할 기회를 제공할 것이다.
메타클래스에 관한 사실
한 가지 도전과제로 시작하고자 한다: 스몰토크 클래스도 객체이므로 무언가의 인스턴스여야 한다. 대체 무엇의 인스턴스일까? 각 객체는 고유의 메타클래스의 인스턴스에 해당한다. 클래스를 팩토리로 간주하면, 메타클래스는 String, Date, Stream, Whale과 같은 일반 팩토리를 생산하는 팩토리로 보면 될 것이다.
메타 클래스에는 두 가지 독특한 특징이 있다:
1. 모든 클래스는 그 메타클래스의 유일한 인스턴스다. 다시 말해, 각 메타클래스는 그 이상도 이하도 아닌 정확히 하나의 인스턴스만 가지며, 그 인스턴스는 일반(ordinary) 클래스이다.
2. 메타 클래스에는 이름이 없다. 따라서 어떤 브라우저에서도 그것을 볼 수 없는 것이다. 그렇다면 스몰토크에서는 어떻게 볼 수 있을까? 스몰토크에서 여느 객체의 클래스를 보는 방법과 같다-객체에게 class 메시지를 전송하고 결과를 표시 또는 검사하면 된다. 따라서 2.7182 class를 표시하면 Float가 생성되고, Whale class를 표시하면 Whale의 메타클래스가 생성된다. 하지만 스몰토크는 이 메타클래스를 어떻게 표시할까? 메타클래스에는 이름이 없다고 말했으므로 이에 대한 답은 만족스럽지 않을 것이다: 스몰토크는 Whale class만 표시한다. 즉, 스몰토크는 원본 메시지를 흉내내기만 한다. 실망스럽든 아니든 이것이 스몰토크가 메시지의 결과가 메타클래스임을 알려주는 방식이다.
❏ 아래 표현식을 하나씩 표시한 결과를 예측하라. 확실치 않다면 실험을 해보라.
'melatonin' class
String class
Penguin new class class
Bird class
Bird class allInstances size
해결책: 당신의 답은 String, String class, Penguin class, Bird class, 1이 된다.
여기까지 완료했다면, "메타클래스도 객체가 아니었던가? 만일 그렇다면 메타클래스 역시 무언가의 인스턴스여야 하지 않은가?"라는 의문이 생길 것이다. 그에 대한 답은 물론 메타클래스도 객체이며, 그 무언가란 Metaclass라는 이름을 가진 클래스를 의미한다. 사실상 Metaclass는 우리가 이야기한 모든 메타클래스를 생성하는 팩토리다. 따라서 Metaclass는 크기가 커야한다.
❏ Metaclass의 인스턴스 수는 얼마나 되는가?
해결책: Metaclass의 인스턴스는 메타클래스 객체이며, 각 일반 클래스마다 정확히 하나만 존재함을 알고 있다. 따라서 스몰토크에서 일반 클래스의 수만큼 Metaclass의 인스턴스도 존재한다. 필자가 사용 중인 VisualAge 버전에서는 인스턴스 수가 약 2100개에 달한다. 자신의 시스템에서 Metaclass allInstances size를 표시하면 메타클래스를 셀 수 있다.
Metaclass에는 이름이 있음을 주목하라. 그것은 일반 클래스다! 그 인스턴스들은 메타클래스라고 알려진 특이한 객체일 뿐이다.
상속
❏ 아래 표현식 각각의 결과를 표시하라.
Integer superclass
Penguin superclass
Penguin new class superclass
Penguin class superclass
해답과 논의: 앞의 두 메시지 결과는 지루하다: Number와 Bird. 하지만 나머지 두 메시지의 결과는 메타클래스의 상속에 관한 중요한 소식이다. Bird와 Bird class 응답은 Penguin의 메타클래스의 슈퍼클래스가 Bird의 메타클래스임을 알려준다. 클래스와 superclass 메시지를 사용해야 하는 다른 예제를 만들어서 실험해보라. 실험은 A가 B의 서브클래스에 해당할 때마다 A의 메타클래스 또한 B의 메타클래스의 서브클래스라는 사실을 확인해줄 것이다. 좀 더 호소력있게 말하자면, 메타클래스의 상속은 클래스의 상속과 유사(parallel)하다.[1] 이러한 결과는 잊어선 안 되므로 Rule P (Parallel에서 땀)라는 이름을 지어주자.
메서드 new
클래스는 객체기 때문에 여느 객체와 마찬가지로 일반 메서드를 가질 것이다. 일반 메서드들은 인스턴스 메서드라고 불리겠지만, 클래스에 대해서는 아직 그 메서드에 대해 특별한 이름, 클래스 메서드라는 이름을 사용해왔다. 따라서 "클래스 메서드"라 함은 클래스의 메타클래스에 해당하는 인스턴스 메서드를 참조하는 간편한 이름표에 지나지 않는다. "Whale의 메타클래스에 대한 인스턴스 메서드, Whale class"보다는 "Whale에 대한 클래스 메서드"라고 이야기하는 편이 더 쉽다[2].
❏ 앞으로 당신이 필요로 할 모든 클래스 메서드들 중에서 가장 중요한 것은 new이다. Bird에 대한 클래스 메서드 new를 작성하고 Penguin에 대해서는 작성하지 않는다고 가정하자. Penguin new 메시지는 어떤 메서드를 실행할 것으로 예상되는가? 그 이유는 무엇인가?
해답: 과거 경험으로 보건대, Bird 클래스에 대한 new 메서드가 실행될 것으로 예상된다. 왜냐면 클래스 메서드는 상속된다고 생각하기 때문이다. 하지만 이제 이 상속에 대한 이유가 있다: 클래스 메서드는 실제로 메타클래스 인스턴스 메서드로서, 여느 인스턴스 메서드와 같이 그 슈퍼클래스로부터 상속될 수 있다. 하지만 Penguin의 메타클래스의 슈퍼클래스는 Bird의 메타클래스이므로 (Rule P) Penguin의 메타클래스에 new 메서드가 없다면, Bird의 메타클래스로부터 new 메서드를 상속한다.
이러한 로직은 꽤 만족스럽다. 클래스 메서드가 상속되는 이유는 Rule P 때문이다. 하지만 여기 충격적인 소식이 있다. 당신이 Animal 계층구조에 어떠한 new 메서드도 작성하지 않는다고 가정하자. Penguin new 메시지는 어떤 메서드를 실행할 것으로 기대하는가? 모두들 실행 가능한 메서드를 먼저 짐작할 것이다-Object에 있는 new 클래스 메서드. 하지만 그러한 메서드는 존재하지 않는다! Object 클래스는 new 메서드 선택자, 즉 인스턴스와 클래스 중 어떤 것도 갖고 있지 않다.
스몰토크에서 기본 new 메서드를 찾기 위해 좀 더 적극적으로 추진할 필요가 있겠다.
전체 그림
인스턴스와 그들의 클래스, 클래스와 그들의 슈퍼클래스 간 개념적 관게를 보여주는 개략도를 모아보겠다. 앞의 17 페이지에서와 같이 간략하게 소개하겠다:
일반 인스턴스, 그들의 클래스, 슈퍼클래스 간 관계는 아래와 같은 모습이다:
객체로서의 클래스, 그들의 클래스, 슈퍼클래스에 대한 병행(parallel) 관계의 모습은 다음과 같다:
각 메타클래스에는 정확히 하나의 인스턴스가 있다; 이러한 인스턴스는 클래스이며, 일반 인스턴스로부터 구별하기 위해 어둡게 칠했다. 밑에서 시작해 윗 방향으로 Penguin, Bird, Animal, Object가 해당한다. 모든 클래스 객체를 편리하게 포함하는 Class라는 클래스의 모양을 주목하라. 그림은 모든 클래스가 Class라는 확실하고 평범한 사실을 보여준다[3].
마지막으로, 메타클래스는 당연히 객체로서 Metaclass라는 클래스에 상주한다. 밑에서부터 윗 방향으로 그린 메타클래스 객체를 Penguin class, Bird class, Animal class, Object class로 생각하면 된다.
그리고 이 모든 그림을 하나로 합친 결과는 다음과 같다:
- 일반 인스턴스
- 인스턴스로서의 클래스 (그 메타클래스의 유일한 인스턴스)
- 인스턴스로서의 메타클래스
이러한 다이어그램 기법의 한 가지 부수적 효과로, VisualAge에서 대략 2100개에 달하는 이 모든 클래스를 하나의 다이어그램에 표현할 경우 각 클래스가 한 번은 서브클래스/슈퍼클래스 관계를 나타내기 위해 직사각형으로, 한 번은 어떤 인스턴스인지 나타내기 위해 색칠된 원으로 표시되어 총 두 번씩 나타날 수도 있다. 이와 비슷하게 모든 메타클래스도 두 번씩, 한 번은 직사각형으로, 한번은 줄무늬 원으로 나타날 수 있다.
추가로 하나의 클래스, Behavior가 나타났음을 주목하라. Behavior는 Class와 Metaclass 둘의 슈퍼클래스다[4]. 계층구조에서 그 이름과 위치를 보면, 우리가 클래스와 같은(class-like) 객체에서 기대하는 모든 행위를 모두 수집함을 알 수 있다.
Behavior는 사실 이번 장을 시작하게 된 질문에 대한 답이 된다: 기본 메서드 new는 Behavior 내 인스턴스 메서드다. 다이어그램을 슬쩍 보면 이것이 new의 훌륭한 위치임을 보여준다. Animal 메타클래스 중 어떤 것도 new 메서드를 구현하지 않을 경우 다이어그램 중간에 메타클래스 계층구조에서의 상속을 보면 Penguin new 메시지의 검색이 결국 기본 new가 실행될 Behavior로 도달할 것임을 알 수 있다.
new 외에 Behavior에 어떤 인스턴스 메서드가 그럴싸하게 위치할 수 있을까? 클래스와 같은 객체 모두에게 적용되는 메서드라면 뭐든 가능할 것이다. Behavior 클래스의 브라우징을 통해 allSubclasses, allSuperclasses, instVarNames, methodDictionary와 같은 메서드뿐만 아니라 이미 사용한 new나 allInstance와 같은 매력적인 메서드들을 찾을 수 있을 것이다.
요약
스몰토크는 순수하다. "모든 것은 객체"이며 모든 객체는 어떤 클래스의 인스턴스다. 심지어 클래스나 메타클래스와 같은 객체조차 어떤 클래스의 인스턴스가 된다. 위의 다이어그램은 인스턴스의 관계를 직사각형 안에 원으로 기록한다. 원이 일반 인스턴스를 표시하든, 클래스를 인스턴스로 표시하든, 아니면 메타클래스를 인스턴스로 표시하든 상관없이 그것을 둘러싼 직사각형이 나타내는 어떤 클래스의 인스턴스라는 사실은 여전하다.
다이어그램은 서브클래스 관계를 중첩된 직사각형으로 기록한다. 클래스든 메타클래스든 상관없이 어떤 클래스와 같은(class-like) 객체든 다이어그램에서는 직사각형으로 나타난다. 그리고 그 슈퍼클래스는 직접 둘러싼 직사각형으로 나타난다.
다이어그램의 좌측과 중간열은 메타클래스 상속이 클래스 상속과 유사한 Rule P를 나타낸다-메타클래스의 슈퍼클래스는 슈퍼클래스의 메타클래스다. Penguin new와 같은 메시지 클래스는 사실 메타클래스의 인스턴스 메시지다. new의 오버라이딩 구현이 없을 경우 그 메시지는 메타클래스 계층구조 최상위의 Behavior 클래스에서 발견된 기본 인스턴스 메서드 new를 실행한다.
연습
적절한 메시지를 이용한 실험을 통해 다이어그램 내 관계를 검증하는 것은 쉬운 문제다. 예를 들어, 다이어그램은 Penguin의 메타클래스가 Bird의 메타클래스의 서브클래스라고 주장한다. 이와 같은 상속 관계를 확인하기 위해서는 superclass 메시지를 사용하라. 따라서 당신은 Penguin class superclass를 표시하고 Bird class의 결과를 기대할 것이다.
마찬가지로 Penguin의 메타클래스는 Metaclass 클래스의 인스턴스라는 다이어그램의 주장과 같이, 인스턴스 관계를 검증하기 위해서는 class 메시지를 사용하라. 그렇다면 Penguin class class를 표시하고 Metaclass의 결과를 기대할 것이다.
❏ 반대로 다이어그램은 class 또는 superclass 메시지의 결과를 예측하도록 도울 수 있다. 다이어그램을 이용해 아래 메시지 각각을 표시한 결과를 예측하라. 물론 실험을 통해 답을 검증할 수도 있다:
Bird new class
Bird new class superclass
Bird class
Bird class superclass
Animal class superclass
Object class superclass
Bird class class
Animal calss class
Object class class
Behavior class
❏ 다이어그램은 완전하지 않다. 큰 스몰토크 이미지는 여기 표시된 19개가 아닌 수십 만개의 live 객체들을 포함한다. 여기서 일반 원, 색칠된 원, 또는 사선 모양의 원으로 표시된 아래의 객체 각각은 다이어그램에서 어디에 위치할까?
- Whale 인스턴스
- String 인스턴스
- Whale
- String
- Whale의 메타클래스
- String의 메타클래스
- Metaclass
- Metaclass의 메타클래스
위의 객체마다 class 메시지를 전송하는 실험을 통해 답을 확인할 수 있다.
Notes
- ↑ 또는 메타클래스 의 슈퍼클래스 는 슈퍼클래스 의 메타클래스다라고 말할 수 있다.
- ↑ 이와 비슷하게, 클래스는 객체기 때문에 고유의 인스턴스 변수를 가질 권리가 있다. 이러한 인스턴스 변수에도 특별한 이름이 있다: 클래스 인스턴스 변수로 알려진다. 따라서 “클래스 인스턴스 변수”는 메타클래스의 일반 인스턴스 변수를 가리키는 데 사용하는 편리한 이름표다. 230 페이지의 솔리테르 패턴에서 클래스 인스턴스 변수를 필요로 한 바 있다.
- ↑ 195 페이지에서 객체 메모리 layout의 연습문제를 해결하면서 Class라 명명된 클래스를 다룬 적이 있을 것이다.
- ↑ IBM Smalltalk와 VisualWorks는 여기에 하나의 클래스가 더 있다-ClassDescription은 Behavior의 서브클래스이자 Class와 Metaclass 둘의 슈퍼클래스이다. 이는 관리를 목적으로 하며, 현재 논의에 어떤 내용도 덧붙이지 않는다.