오브젝트 독서회 11회차 - 12장 다형성

이번에도 @vincent @은딩 @freebear 님께서 참여해주셨습니다.


P389

상속의 목적은 코드 재사용이 아니다. 상속은 타입 계층을 구조화하기 위해 사용해야 한다.

@은딩 @vincent

상속을 이용해 자식 클래스를 추가하려 한다면 스스로에게 다음과 같은 질문을 해보기 바란다. 상속을 사용하려는 목적이 단순히 코드를 재사용하기 위해서인가? 아니면 클라이언트 관점에서 인스턴스들을 동일하게 행동하는 그룹으로 묶기 위해서인가? 첫 번째 질문에 대한 답이 ‘예’라면 상속을 사용하지 말아야 한다.

@freebear

많은 시간이 흐른 지금도 여전히 상속은 다형성을 구현할 수 있는 가장 일반적인 방법이다. 하지만 최근의 언어들은 상속 이외에도 다형성을 구현할 수 있는 다양한 방법을 제공하고 있기 때문에 과거에 비해 상속의 중요성이 많이 낮아졌다고 할 수 있다.

@은딩

이번 장에서는 상속의 관점에서 다형성이 구현되는 기술적인 메커니즘을 살펴보기로 한다. 이번 장을 읽고 나면 다형성이 런타임에 메시지를 처리하기에 적합한 메서드를 동적으로 탐색하는 과정을 통해 구현되며, 상속이 이런 메서드를 찾기 위한 일종의 탐색 경로를 클래스 계층의 형태로 구현하기 위한 방법이라는 사실을 이해하게 될 것이다.

@freebear

P390

다형성(Polymorphism) 이라는 단어는 그리스어에서 '많은’을 의미하는 'poly’와 '형태’를 의미하는 'morph’의 합성어로 '많은 형태를 가질 수 있는 능력’을 의미한다.

@vincent

컴퓨터 과학에서는 다형성을 하나의 추상 인터페이스에 대해 코드를 작성하고 이 추상 인터페이스에 대해 서로 다른 구현을 연결할 수 있는 능력으로 정의한다[Czarnecki00].

@은딩

간단하게 말해서 다형성은 여러 타입을 대상으로 동작할 수 있는 코드를 작성할 수 있는 방법이라고 할 수 있다.

@freebear

P391

포함 다형성은 메시지가 동일하더라도 수신한 객체의 타입에 따라 실제로 수행되는 행동이 달라지는 능력을 의미한다. 포함 다형성은 서브타입(Subtype) 다형성이라고 부른다.

@은딩

포함 다형성은 객체지향 프로그래밍에서 가장 널리 알려진 형태의 다형성이기 때문에 특별한 언급 없이 다형성이라고 할 때는 포함 다형성을 의미하는 것이 일반적이다.

@freebear

P392

포함 다형성을 구현하는 가장 일반적인 방법은 상속을 사용하는 것이다.

@은딩 @freebear

포함 다형성을 서브타입 다형성이라고 부른다는 사실에서 예상할 수 있겠지만 포함 다형성을 위한 전제조건은 자식 클래스가 부모 클래스의 서브타입이어야 한다는 것이다. 그리고 상속의 진정한 목적은 코드 재사용이 아니라 다형성을 위한 서브타입 계층을 구축하는 것이다.

@은딩 @freebear

상속의 일차적인 목적이 코드 재사용이 아닌 서브타입의 구현이라는 사실을 이해하는 것만으로도 충분하다.

@은딩

P400

상속을 인스턴스 관점에서 바라볼 때는 개념적으로 자식 클래스의 인스턴스 안에 부모 클래스의 인스턴스가 포함되는 것으로 생각하는 것이 유용하다.

@freebear

P401

데이터 관점의 상속이 자식 클래스의 인스턴스 안에 부모 클래스의 인스턴스를 포함하는 개념이라면 행동 관점의 상속은 부모 클래스가 정의한 일부 메서드를 자식 클래스의 메서드로 포함시키는 것을 의미한다.

@freebear

각 객체는 자신의 클래스인 Lecture의 위치를 가리키는 class라는 이름의 포인터를 가지며 이 포인터를 이용해 자신의 클래스 정보에 접근할 수 있다.

@은딩

P402

자식 클래스에서 부모 클래스로의 메서드 탐색이 가능하기 때문에 자식 클래스는 마치 부모 클래스에 구현된 메서드의 복사본을 가지고 있는 것처럼 보이게 된다.

@freebear

따라서 각 객체에 포함된 class 포인터와 클래스에 포함된 parent 포인터를 조합하면 현재 인스턴스의 클래스에 최상위 부모 클래스에 이르기까지 모든 부모 클래스에 접근하는 것이 가능하다.

@은딩

P407

컴파일러 관점에서 자식 클래스는 아무런 제약 없이 부모 클래스를 대체할 수 있기 때문에 부모 클래스와 협력하는 클라이언트는 다양한 자식 클래스의 인스턴스와도 협력하는 것이 가능하다. 여기서 자식 클래스는 현재 상속 계층에 존재하는 자식 클래스뿐만 아니라 앞으로 추가될지도 모르는 미래의 자식 클래스들을 포함한다. Lecture의 모든 자식 클래스는 evaluate 메시지를 이해할 수 있기 때문에 Professor는 Lecture를 상속받는 어떤 자식 클래스와도 협력할 수 있는 무한한 확장 가능성을 가진다. 따라서 이 설계는 유연하며 확장이 용이하다.

@은딩 @freebear

P410

메시지는 상속 계층을 따라 부모 클래스에게 자동으로 위임된다[Metz12].

@은딩

P416

그리고 이 동적인 문맥을 결정하는 것은 바로 메시지를 수신한 객체를 가리키는 self 참조다.

@vincent

P417

self 참조가 동적 문맥을 결정한다는 사실은 종종 어떤 메서드가 실행될지를 예상하기 어렵게 만든다. 대표적인 경우가 자신에게 다시 메시지를 전송하는 self 전송(self send) 이다.

@은딩

다시 한번 강조하겠다. 현재 클래스의 메서드를 호출하는 것이 아니라 현재 객체에게 메시지를 전송하는 것이다.

@vincent

P419

self 전송은 자식 클래스에서 부모 클래스 방향으로 진행되는 동적 메서드 탐색 경로를 다시 self 참조가 가리키는 원래의 자식 클래스로 이동시킨다.

@은딩

P421

이해할 수 없는 메시지를 처리할 수 있는 동적 타입 언어는 좀 더 순수한 관점에서 객체지향 패러다임을 구현한다고 볼 수 있다. 협력을 위해 메시지를 전송하는 객체는 메시지를 수신한 객체의 내부 구혛ㄴ에 대해서는 알지 못한다.

@은딩 @vincent

동적 타입 언어는 이해할 수 없는 메시지를 처리할 수 있는 능력을 가짐으로써 메시지가 선언된 인터페이스와 메서드가 정의된 구현을 분리할 수 있다.

@vincent

P422

이해할 수 없는 메시지를 처리할 수 있는 동적 타입 언어의 특징은 메타 프로그래밍 영역에서 진가를 발휘한다. 특히 동적 타입 언어의 이러한 특징으로 인해 동적 타입 언어는 정적 타입 언어보다 더 쉽고 강력한 도메인-특화 언어(Domain-Specific Language, DSL) 를 개발할 수 있는 것으로 간주된다. 마틴 파울러는 동적 타입 언어의 이러한 특징을 이용해 도메인-특화 언어를 개발하는 방식을 동적 리셉션(dynamic reception)[Fowler10]이라고 부른다.

@vincent

self 참조의 가장 큰 특징은 동적이라는 점이다.

@은딩

self의 이런 특성과 대비해서 언급할 만한 가치가 있는 것이 바로 super 참조(super reference)다.

@은딩

자식 클래스에서 부모 클래스의 구현을 재사용해야하는 경우가 있다.

@은딩

P424

사실 super 참조의 용도는 부모 클래스에 정의된 메서드를 실행하기 위한 것이 아니다. super 참조의 정확한 의도는 '지금 이 클래스의 부모 클래스에서부터 메서드 탐색을 시작하세요’다. 만약 부모 클래스에서 원하는 메서드를 찾지 못한다면 더 상위의 부모클래스로 이동하면서 메서드가 존재하는지 검사한다.

@vincent @은딩

P425

일단 self 참조가 동적인 문맥을 결정한다는 사실을 이해하고 나면 상속을 바라보는 새로운 시각이 형성된다. 바로 자식 클래스에서 부모 클래스로 self 참조를 전달하는 메커니즘으로 상속을 바라보는 것이다.

@vincent

P429

위임은 본질적으로 자신이 정의하지 않거나 처리할 수 없는 속성 또는 메서드의 탐색 과정을 다른 객체로 이동시키기 위해 사용한다. 이를 위해 위임은 항상 현재의 실행 문맥을 가리키는 self 참조를 인자로 전달한다.

@은딩

P430

객체지향 시스템에서 지식 공유를 위해 프로토타입 접근법을 구현하는 것은 위임이라고 불리는 대체 메커니즘으로… 위임은 클래스와 인스턴스 간의 차이를 제거한다. 어떤 객체도 프로토타입이 될 수 있다.

@vincent

상속은 동적으로 메서드를 탐색하기 위해 현재의 실행 문맥을 가지고 있는 self 참조를 전달한다. 그리고 이 객체들 사이에서 메시지를 전달하는 과정은 자동으로 이뤄진다. 따라서 자동적인 메세지 위임이라고 부르는 것이다.

@은딩 @vincent

우리는 클래스가 아닌 객체를 이용해서도 상속을 흉내 낼 수 있다는 사실을 알게 됐다. 사실 클래스가 존재하지 않고 오직 객체만 존재하는 프로토타입 기반의 객체지향 언어에서 상속을 구현하는 유일한 방법은 객체 사이의 위임을 이용하는 것이다.

@vincent

P431

prototype 은 앞에서 위임을 직접 구현했던 예제에서 부모 객체를 가리키기 위해 사용했던 인스턴스 변수 @parent와 동일한 것으로 봐도 무방하다. 차이점이라면 prototype은 언어 차원에서 제공되기 때문에 self 참조를 직접 전달하거나 메시지 포워딩을 번거롭게 직접 구현할 필요가 없다는 점이다.

@vincent

P433

자바스크립트는 prototype으로 연결된 객체들의 체인을 거슬러 올라가며 자동적으로 메시지에 대한 위임을 처리한다.

@은딩

자바스크립트에는 클래스가 존재하지 않기 때문에 오직 객체들 사이의 메시지 위임만을 이용해 다형성을 구현한다. 이것은 객체지향 패러다임에서 클래스가 필수 요소가 아니라는 점을 잘 보여준다. 또한 상속 이외의 방법으로도 다형성을 구현할 수 있다는 사실 역시 잘 보여준다.

@freebear @vincent

P434

객체지향은 객체를 지향하는 것이다. 클래스는 객체를 편리하게 정의하고 생성하기 위해 제공되는 프로그래밍 구성 요소일 뿐이며 중요한 것은 메시지와 협력이다. 클래스 없이도 객체 사이의 협력 관계를 구축하는 것이 가능하며 상속 없이도 다형성을 구현하는 것이 가능하다.

@은딩

현재 대부분의 객체지향 언어들이 클래스에 기반하고 있기 때문에 다형성을 위해 클래스 기반의 상속이 널리 사용되지만 프로토타입 언어처럼 위임을 통해 객체 수준에서 상속을 구현하는 언어들도 존재한다는 사실을 기억하기 바란다. 심지어 클래스 기반의 객체지향 언어를 사용하고 있더라도 클래스라는 제약을 벗어나기 위해 위임 메커니즘을 사용할 수 있다.

@vincent

이 사실을 이해하면 다형성과 상속, 나아가 객체지향 언어를 바라보는 여러분의 시각이 달라질 것이다.

@freebear

6개의 좋아요