다이아몬드 상속
다이아몬드 상속(Diamond Problem)은 다중 상속을 허용하는 언어에서 상속 관계가 다이아몬드 형태가 될 때 발생하는 모호성(Ambiguity) 문제이다.
- 구조: 네 개의 클래스 A, B, C, D가 있을 때, B와 C가 A를 상속하고, D가 B와 C를 동시에 상속하는 형태이다.
문제 발생 메커니즘
문제 발생 과정을 보여주는 내용은 다음과 같다.
-
최상위 클래스: A에 methodX() 메소드가 정의되어 있다.
-
중간 클래스: B와 C가 각각 이 methodX() 메소드를 자신만의 방식으로 오버라이딩한다.
-
최하위 클래스: D가 B와 C를 모두 상속받을 때, D가 methodX() 메소드를 호출하면, B의 구현과 C의 구현 중 어느 것을 실행해야 할지 컴파일러가 판단할 수 없는 모호성이 발생한다.
C#, 자바 언어의 해결 전략: 클래스의 단일 상속
C# 언어와 자바 언어는 다이아몬드 문제를 회피하고 코드의 안정성을 보장하기 위해 클래스의 다중 상속을 원천적으로 금지한다.
-
원칙: C# 또는 자바의 클래스는 오직 하나의 부모 클래스만 상속받을 수 있다. 따라서 extends 키워드는 한 번만 사용이 가능하다.
-
이유: 모호한 경로를 허용하지 않아 프로그램의 예측 가능성과 안정성을 최우선으로 확보하기 위함이다.
자바의 인터페이스를 통한 다중 구현
자바는 다중 상속의 장점인 다형성과 기능 결합을 잃지 않기 위해 인터페이스의 다중 구현을 허용하며, 인터페이스는 구현체(Method Body)를 상속하지 않기 때문에 충돌이 발생하지 않습니다.
- 예외 (Java 8 이후): 인터페이스의 default 키워드를 이용한 메소드 충돌 시, 컴파일러는 오류를 발생시키고 최종 클래스에서 해당 메소드를 반드시 오버라이딩하여 모호성을 명확히 해결하도록 강제한다.
Python 언어의 해결 전략: 메소드 결정 순서
파이썬은 클래스의 다중 상속을 허용하며, 다이아몬드 상속 문제를 해결하기 위해 메소드 결정 순서(Method Resolution Order, MRO)라는 규칙을 사용한다.
-
원칙: C3 선형화(C3 Linearization) 알고리즘을 사용하여 일관된 MRO를 결정한다.
-
해결: 인터프리터는 미리 계산된 MRO 목록 순서에 따라 부모 클래스들을 탐색하며, 가장 먼저 해당 메소드를 가진 클래스의 구현을 실행하여 모호성을 제거한다.
예제 코드는 다음과 같다.
class Type(type):
def __repr__(cls):
# repr(O)가 '<main.O>' 대신 'O'가 되도록 만들 것이며,
# O의 모든 자식 클래스에도 동일하게 적용된다.
return cls.__name__
# 기본 클래스 정의
class A(O): pass
class B(O): pass
class C(O): pass
class D(O): pass
class E(O): pass
# 상속 트리 구성
class K1(C, A, B): pass
class K3(A, D): pass
class K2(B, D, E): pass
class Z(K1, K3, K2): pass
# >>> Z.mro() 메소드의 결과로,
# [Z, K1, C, K3, A, K2, B, D, E, O, <class 'object'>] 트리를 가질 수 있다.
print(Z.mro())
C3 선형화 알고리즘 예제에 대한 종속성 그래프는 다음과 같다.
C++ 언어의 해결 전략: 가상 상속
C++은 클래스의 다중 상속을 허용하고 가상 상속(Virtual Inheritance)을 제공하며, 다음과 같은 특징이 있다.
class B: virtual public A {};
class C: virtual public A {};
-
데이터 중복 문제: 중간 클래스(B, C)가 최상위 클래스(A)를 상속받을 때 virtual 키워드를 사용하고 최하위 클래스가 존재한다면, A 클래스의 멤버를 공유된 단일 객체로 받도록 강제하여 데이터 중복 문제를 제거한다.
-
메소드 모호성: 메소드 모호성이 발생하면 컴파일 오류를 발생시키고, 최하위 클래스에서 해당 메소드를 직접 오버라이딩하여 사용할 부모의 버전을 명확히 지정하도록 강제한다.
요약
| 언어 | 다중 클래스 상속 | 해결 전략 | 모호성 해결 매커니즘 |
|---|---|---|---|
| Java | 금지 | 단일 상속 및 다중 인터페이스 구현 | 인터페이스의 코드 부재 또는 default 키워드 메소드 충돌 시 필수 오버라이딩을 강제한다. |
| C# | 금지 | 단일 상속 및 다중 인터페이스 구현 | 자바와 동일하게 클래스의 단일 상속을 강제하여 문제를 원천 차단한다. |
| Python | 허용 | 메소드 결정 순서(Method Resolution Order, MRO) | C3 선형화를 통해 명확한 탐색 순서를 결정하고, 가장 먼저 찾은 구현을 실행한다. |
| C++ | 허용 | 가상 상속(Virtual Inheritance) | 데이터의 중복을 방지하며, 메소드 모호성은 컴파일 오류 및 직접 오버라이딩을 강제한다. |