노트

상속보다 위임

Composition over Inheritance

설계#refactoring · 연결된 개념 11개

쉽게 말하면

상속보다 위임은 기능이 필요할 때 부모 것을 통째로 물려받기보다 잘하는 사람에게 맡기라는 말이에요. 유산은 원치 않는 것까지 딸려 오지만, 부탁은 필요한 일만 골라서 할 수 있죠.

비유가 깨지는 곳 상속을 아예 쓰지 말라는 말은 아니에요. 파울러는 단순한 상속으로 시작하고, 한 축만 고를 수 있거나 부모를 고치면 자식이 깨지는 문제가 생길 때 위임으로 갈아타라고 해요.

"클래스 상속보다 객체 합성(위임)을 써라"는 원칙과, 상속 관계를 위임으로 바꾸는 두 리팩터링. 상속을 쓰지 말라는 뜻이 아니라 과용에 대한 반작용으로 나온 말이다.

상속의 두 가지 한계

  • 한 축만 고를 수 있다: 사람을 나이대와 소득 수준으로 동시에 나누고 싶어도 서브클래스는 한 기준만 택할 수 있다
  • 강하게 결합된다: 부모를 고치면 자식이 깨지기 쉽고, 부모와 자식이 다른 팀·모듈에 있으면 더 위험하다

위임은 둘 다 해결한다. 여러 이유로 여러 객체에 위임할 수 있고, 위임 관계는 필요한 인터페이스만으로 이어진다.

두 리팩터링

  • 슈퍼클래스를 위임으로(Replace Superclass with Delegate): 부모의 기능 중 자식에게 맞지 않는 게 많을 때. 유명한 나쁜 예가 Java의 Stack extends Vector다. 스택에 어울리지 않는 리스트 연산이 전부 노출된다. 리스트를 필드로 두고 필요한 기능만 전달하는 편이 낫다
  • 서브클래스를 위임으로(Replace Subclass with Delegate): 변형 동작을 별도 위임 객체에 담아 부모가 그 객체를 들고 있게 한다. 상속 축을 다른 용도로 비워 둘 수 있다

상속 포기(Refused Bequest) 냄새

자식이 부모의 동작은 쓰면서 인터페이스는 따르고 싶지 않다면 상속이 잘못된 것이다. 부모가 쓰이는 모든 자리에 자식을 넣어도 문제없어야 한다는 리스코프 치환 원칙(Liskov Substitution Principle)을 깨기 때문이다.

파울러의 조언은 실용적이다. 처음에는 단순한 상속으로 시작하고, 문제가 생기면 위임으로 갈아탄다. "어느 하나만 고집하지 말고 적절히 섞어 쓰라"는 것이다. 전략 패턴과 데코레이터 패턴은 합성을 쓰는 대표 패턴이고, React가 상속 대신 합성을 권하는 것도 같은 흐름이다(컴파운드 컴포넌트 패턴, 믹스인).

출처: 『리팩터링 2판』 마틴 파울러 (원서 Refactoring, 2nd Edition) · refactoring.com: Replace Superclass with Delegate · refactoring.com: Replace Subclass with Delegate · Refactoring.Guru: Refused Bequest

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • 의존성 역전 원칙

    고수준 모듈(업무 규칙)이 저수준 모듈(데이터베이스(DB), HTTP(Hypertext Transfer Protocol) 클라이언트 같은 세부 구현)에 직접 의존하지 않고, 둘 다 추상화에 의존해야 한다는 원칙. 의존 방향이 "업무 → 세부"에서 "세부 → 추상화 ← 업무"로 뒤집힌다.

  • 공변성과 반공변성

    공변성(Covariance)과 반공변성(Contravariance)은 Dog가 Animal의 서브타입일 때, 그 타입을 감싼 타입(배열, 함수)의 서브타입 관계가 같은 방향으로 유지되는지(공변) 아니면 뒤집히는지(반공변)를 말한다.

  • 매개변수 객체 만들기

    여러 함수에 늘 함께 넘겨지는 값 묶음(데이터 뭉치, Data Clumps)을 하나의 객체로 묶어 넘기는 리팩터링.

  • 기본형 집착

    전화번호·금액·우선순위 같은 도메인 개념을 끝까지 문자열이나 숫자로만 다루는 냄새. 같은 검증과 포맷 코드가 여기저기 반복되고, 문자열로 모든 걸 표현하는 "stringly typed" 코드가 된다.

  • 커맨드 패턴

    요청이나 작업을 실행 가능한 객체로 포장하는 패턴. 작업을 값처럼 저장·전달·대기열에 넣을 수 있게 되고, 되돌리기 같은 부가 연산을 붙일 수 있다.

보기 옵션