서적/Object

13장 서브 클래싱과 서브 타이핑

Mo_bi!e 2026. 3. 26. 20:59

13장: 상속의 본질 - 타입 계층과 다형성

상속의 진정한 목적이 코드 재사용이 아니라 '타입 계층'을 구축하는 것

1. 상속의 두 얼굴: 서브클래싱 vs 서브타이핑

책에서는 상속을 사용하는 용도를 엄격하게 두 가지로 구분한다.

  • 서브클래싱 (Subclassing - 구현 상속)
    • 단순히 부모 클래스의 코드를 재사용하기 위해 상속을 사용하는 경우를 말한다.
    • 이는 부모와 자식 간의 결합도를 높여 변경에 취약한 설계를 만든다.
      • 10장에서 비판했던 방식이 바로 이 서브클래싱이다.
  • 서브타이핑 (Subtyping - 인터페이스 상속)
    • 부모 클래스가 정의한 타입 계층을 물려받아 다형적으로 동작하기 위해 상속을 사용하는 것을 의미한다.
    • 객체지향 설계에서 상속을 사용하는 유일하고 올바른 목적이다.

 

2. 대체 가능성을 결정하는 기준: 리스코프 치환 원칙 (LSP)

올바른 상속(서브타이핑)인지 판단하는 기준은 SOLID 원칙 중 하나인 리스코프 치환 원칙(LSP)이다.

    • 행동 호환성: 자식 클래스는 부모 클래스의 역할을 완벽하게 대체할 수 있어야 한다.
    • 클라이언트 관점의 중요성: 상속 관계의 적절성은 현실 세계의 어휘적 분류(is-a)가 아니라, 해당 객체를 사용하는 클라이언트의 기대에 따라 결정된다.

예: 펭귄은 새인가?
현실에서는 펭귄이 새(Bird)이지만, 소프트웨어 클라이언트가 새에게 '날기(fly)’를 기대한다면 펭귄은 새를 상속받아서는 안 된다. 펭귄은 날 수 없으므로 클라이언트의 기대를 저버리기 때문이다.

  • 결론: 상속 관계가 올바른지 판단할 때는 반드시 문장 앞에 "클라이언트 입장에서"라는 전제를 붙여야 한다. 직사각형과 정사각형 예시 역시 클라이언트의 기대를 배신하므로 상속을 사용해서는 안 되는 대표적 사례다.

 

3. 계약에 의한 설계 (Design by Contract)

리스코프 치환 원칙을 더 엄밀하게 설명하기 위해 '계약'이라는 개념을 도입한다. 클라이언트와 부모 클래스 사이의 약속을 자식이 어떻게 지켜야 하는지를 규정한다.

  • 사전조건 강화 불가: 자식 클래스는 부모 클래스보다 더 까다로운 조건(사전조건)을 클라이언트에게 요구해서는 안 된다. 클라이언트가 부모 수준의 조건만 맞췄음에도 자식이 에러를 던지면 시스템은 붕괴된다.
  • 사후조건 완화 불가: 자식 클래스는 부모 클래스가 보장해주던 결과(사후조건)를 더 약하게 보장해서는 안 된다. 클라이언트는 부모가 보장하던 최소한의 결과를 얻으리라 믿고 코드를 작성하기 때문이다.

 

정리하며
결국 상속은 코드를 편하게 쓰기 위한 수단이 아니라, 클라이언트의 관점에서 동일하게 행동하는 객체들의 집합(타입 계층)을 만들기 위해 사용해야 한다