앞서 상속은 코드 재사용을 목적으로 사용하면 여러 가지 문제점들을 야기한다고 설명했다. 상속의 주목적은 타입 계층 구축이다. 이를 기반으로 다형성이 구현된다.

곧, 다형성은 상속을 기반으로 동작하는 것이라 볼 수 있다. 다형성이 구현되는 매커니즘에 대해 살펴보자.

다형성


다형성은 런타임 시점(메시지를 처리할 수 있는 시점)에 적절한 메서드를 탐색하는 과정을 통해 구현된다.

상속은 이러한 메서드를 찾기 위한 일종의 탐색 경로를 클래스 계층의 형태로 구현하는 방법이다.

종류

  • 매개 변수 다형성
    • 제네릭 프로그래밍과 유사
  • 포함(서브타입) 다형성
    • 수신한 객체의 타입에 따라 실제로 수행되는 행동이 달라지는 것.
    • 가장 일반적인 다형성
  • 오버로딩
    • 유사한 작업을 수행하는 메서드의 이름을 통일하고 파라미터를 다르게 하는 방법
  • 강제
    • 언어가 지원하는 강제 캐스팅이나 사용자가 직접 구현한 캐스팅을 이용해 동일한 연산자를 다양한 타입에 사용하는 방식
      e.g. Java의 + 연산자

포함 다형성

Movie.java

public Money calculateMovieFee(Screening screening) {
	return fee.minus(discountPolicy.caculateDiscountAmount(screening));
}
  • DiscountPolicy 구현체에 따라 행동이 달라진다.

상속은 클래스들을 계층으로 쌓아올린 후 상황에 따라 적절한 메서드를 선택할 수 있는 매커니즘을 제공한다. 따라서, 포함 다형성은 대부분 상속을 사용한다.

중요
상속의 목적은 코드 재사용이 아닌 서브타입 계층을 구축하는 것이다.
타입 계층을 고려하지 않고 재사용을 위해 상속을 사용하면 유지보수 및 직관성이 안좋은 코드를 얻게 된다.

전제조건은 자식 클래스가 부모 클래스의 서브타입이여야 한다는 것이다. 이 경우 아래 3가지 기준을 통해 메서드를 선택한다.

  • 어떤 메시지를 수신했는지?
  • 어떤 클래스의 인스턴스 인지?
  • 상속 계층이 어떻게 구성되는지?

상속의 양면성


다시 한 번 말하지만 상속의 목적은 코드 재사용이 아닌 타입 계층 구축이다. 이를 이해하기 위해 데이터-행동 관점으로 상속을 살펴보자.

  • 데이터 관점 : 부모 클래스의 모든 데이터가 자식 인스턴스에 포함
  • 행동 관점 : 부모 클래스에서 정의한 일부(private 제외) 메서드가 자식 인스턴스에 포함

단순히 데이터-행동 관점으로 바라보면 재사용 매커니즘으로 보인다. 이 관점은 상속을 오해한 것이다.

상속을 사용하기 위해선 타입 계층에 대한 고민을 해야한다. 이를 위해 상속을 언제 사용해야 하는지 이해해야 하며 이를 고려하여 행동-데이터 관점을 생각해야 한다.

올바른 데이터 관점의 상속

자식 클래스의 인스턴스 안에 부모 클래스의 인스턴스를 포함하는 것으로 설명할 수 있다. 결국 자식 클래스의 인스턴스는 자동으로 부모 클래스에 정의한 모든 인스턴스 변수를 내부에 포함한다.

올바른 행동 관점에서 상속

실제로 상속을 받는 객체가 부모의 모든 퍼블릭 인터페이스를 포함하는건 아니다. 부모의 퍼블릭 인터페이스는 별도로 존재하며 포인터를 통해 이를 연결한다.

자식 인스턴스에서 메서드를 선택하는 과정을 아래와 같다.

  1. 메시지를 수신한 객체는 먼저 자신을 생성한 클래스에 적절한 메서드를 찾는다.
    컴파일러는 self 참조라는 임시 변수를 자동 생성 후 메시지를 수신한 객체를 가리키도록 설정
  2. 없다면 상속 계층을 따라 부모 클래스들에서 적절한 메서드를 찾는다.
    self가 가리키는 객체의 클래스를 상속 계층의 역방향으로 바꿔가며 진행
  3. 상속 계층의 최상위 클래스에 이르렀지만 메서드를 발견하지 못한 경우 예외가 발생한다.

즉, 자식 -> 부모 순으로 탐색되므로 자식 클래스에 선언된 메서드가 더 높은 우선순위를 갖는다. 이 매커니즘 덕에 업캐스팅, 동적 바인딩이 구현된다.

참고 동적 메서드 탐색
부모 클래스의 타입에 대해 메시지를 전송하더라도 실행시에는 실제 클래스 기반으로 실행될 메서드가 선택되게 해준다.

OCP의 의도는 코드를 변경하지 않아도 기능을 추가할 수 있게 해주는 것이다. 유연하고 확장 가능한 코드를 만들도록 의존관계를 구조화한다.

업캐스팅과 동적 메서드 탐색은 상속을 이용해 OCP를 따르는 코드를 작성할 때 하부에서 동작하는 기술적 내부 매커니즘을 설명한다.

참고
그렇다고 추상화에 의존하지 않으면 DIP는 물론 OCP도 지키지 못한다 볼 수 있다.

자동적인 메시지 위임

동적 메서드 탐색에서 상속 계층은 메시지를 수신한 객체가 자신이 이해할 수 없는 메시지를 부모 클래스에 전달하기 위한 물리적인 경로를 정의하는 것으로 볼 수 있다.

상속 계층내에서 클래스는 메시지를 처리할 방법을 못찾으면 이를 부모 클래스에게 위임한다. 핵심은 적절한 메서드를 찾을 때 까지 상속 계층을 따라 부모 클래스로 처리가 위임된다는 것이다.

따라서, 상속 계층을 정의한다는 건 메서드 탐색 경로를 정의한다는 것과 동일하다 볼 수 있다.

동적인 문맥

self 전송이란 자기 자신에게 메시지를 전송하는 것을 말한다. 대표적으로 Java의 this 키워드 사용이 있다.

self 전송은 자식 클래스에서 부모 클래스 방향으로 진행되는 동적 메서드 탐색 경로를 다시 self 참조가 가리키는 원래의 자식 클래스로 이동시킨다. 이로 인해 실행될 메서드를 결정할 때 상속 계층 전체를 훑어가며 코드를 이해해야 하는 상황이 발생할 수 있다.

결과적으로 self 전송이 깊은 상속 계층과 중간중간 메서드 오버라이딩이 만나면 극단적으로 이해하기 어려운 코드가 만들어진다.

이해할 수 없는 메시지

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

이러한 관점으로 봤을 때 객체지향 패러다임에 더 적합하다 볼 수 있다. 그러나, 코드 이해와 수정이 어렵고 디버깅 과정이 복잡하다.

객체지향 프로그래밍 언어는 이와 같은 매커니즘의 도움을 받아 동일한 메시지에 대해 서로 다른 메서드를 호출할 수 있는 다형성을 구현한다.

반면, 정적 타입 언어는 컴파일 시점에 현 객체가 메시지를 처리할 수 있는지 여부를 확인한다. 좀 더 안정적이나 유연성이 부족하다.

self vs super

self 참조의 가장 큰 특징은 동적이라는 것이다. 덕분에 메서드 탐색을 위한 문맥을 실행 시점에 결정한다.

반면, 자식 클래스에서 부모 클래스의 구현을 재사용해야 되는 경우가 있다. 대부분 객체지향 언어에선 super 참조를 통해 부모 클래스의 구현을 제공한다.

그러나 super 참조가 언제나 현 객체의 부모 클래스의 구현을 제공한다고 보장할 순 없다. self 와 동일한 탐색 과정을 거치므로 더 상위에 위치한 조상 클래스의 메서드일 수 있다.

참고
super 참조는 "이 클래스의 부모 클래스부터 탐색을 시작한다."를 뜻한다.

상속 vs 위임


지금까지 살펴본 다형성은 self 참조가 가리키는 현 객체에게 메시지를 전달하는 특성을 기반으로 동작한다. 동일한 타입의 객체 참조에게 동일하게 메시지를 전송하더라도 self가 뭘 가리키느냐에 따라 메서드 탐색을 위한 문맥이 달라진다.

자신이 수신한 메시지를 다른 객체에게 동일하게 전달해 처리를 요청하는 행위를 말한다. 본질적으로 자신이 정의하지 않거나 처리할 수 없는 속성 또는 메서드 탐색을 다른 객체로 이동시키기 위해 사용한다.

이를 위해 위임은 항상 현재 실행 문맥이 가리키는 self 참조를 인자로 전달한다. 결과적으로 자식-부모 인스턴스 간 실행 문맥을 공유할 수 있게 된다.

참고 포워딩 vs 위임
포워딩도 위임과 비슷한 목적으로 사용되나 self 참조를 전달하지 않는다는 차이점이 있다.
self 를 전달하지 않는다는 건 단순히 코드를 재사용하고 싶은 경우라 볼 수 있다.

상속은 동적으로 메서드 탐색을 위해 현재의 실행 문맥을 가지고 있는 self 참조를 전달한다. 그리고 이 객체들 사이에서 메시지를 전달하는 과정이 자동으로 이뤄진다. 따라서, 상속을 자동적 메시지 위임이라 볼 수 있다. 상속은 단순히 클래스 사이의 정적인 관계로만 구현되는 건 아니다.

0개의 댓글