노트

조건부 로직을 다형성으로 바꾸기

Replace Conditional with Polymorphism

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

쉽게 말하면

조건부 로직을 다형성으로 바꾸기는 지휘자가 '바이올린이면 이렇게, 첼로면 저렇게' 일일이 지시하는 대신, '연주하세요' 한마디에 각 연주자가 자기 악기로 알아서 연주하게 하는 거예요.

비유가 깨지는 곳 분기가 한두 곳뿐이면 판별 유니언과 switch가 더 단순할 수 있어요. 진짜 신호는 같은 기준의 switch가 여러 함수에 반복될 때고, 동작이 런타임에 바뀌면 상태·전략 패턴으로 객체를 갈아 끼워요.

타입에 따라 갈라지는 조건문을, 타입마다 클래스(또는 객체)를 두고 각자 자기 방식으로 처리하게 바꾸는 리팩터링.

// before
function plumage(bird: Bird) {
  switch (bird.type) {
    case 'european': return '보통'
    case 'african': return bird.coconuts > 2 ? '지침' : '보통'
    default: return '알 수 없음'
  }
}
 
// after
class EuropeanSwallow { get plumage() { return '보통' } }
class AfricanSwallow { constructor(private coconuts: number) {}
  get plumage() { return this.coconuts > 2 ? '지침' : '보통' } }
  • 반복되는 switch(Repeated Switches) 냄새: 같은 기준의 switch가 여러 함수에 있으면, 새 타입이 생길 때 모두 찾아 고쳐야 한다. 이 리팩터링의 가장 분명한 신호다. 심플 디자인이 말하는 "확산되는 if 조건의 중복"과 같다
  • 기본 동작 + 변형: 공통 동작은 부모에, 변형만 자식에 둔다
  • 알맞은 인스턴스를 골라 주는 팩터리 함수(Factory Function)를 함께 만든다(팩토리 메서드 패턴)
  • 타입 코드를 서브클래스로 바꾸는 것(Replace Type Code with Subclasses)이 대개 선행 단계다. 반대로 쓰이지 않게 된 서브클래스는 필드로 되돌린다(Remove Subclass)
  • 동작이 런타임에 바뀐다면 상속 대신 상태 패턴이나 전략 패턴으로 객체를 갈아 끼운다

JavaScript·TypeScript에서는 같은 메서드만 있으면 같은 타입처럼 쓸 수 있어(구조적 타이핑) 상속 계층 없이도 다형성을 쓸 수 있다. 분기가 한두 곳뿐이라면 판별 유니언(Discriminated Union)과 switch가 더 단순한 선택일 수 있다.

출처: 『리팩터링 2판』 마틴 파울러 (원서 Refactoring, 2nd Edition) · refactoring.com: Replace Conditional with Polymorphism · Refactoring.Guru: Replace Conditional with Polymorphism · Refactoring.Guru: Switch Statements

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • 반복문을 파이프라인으로 바꾸기

    for 반복문을 filter·map·reduce 같은 컬렉션 연산의 연쇄, 즉 컬렉션 파이프라인(Collection Pipeline)으로 바꾸는 리팩터링. 각 원소가 어떤 단계를 거치는지가 위에서 아래로 읽힌다.

  • 보호 구문

    정상 흐름이 아닌 경우를 함수 앞부분에서 검사하고 바로 빠져나오는 조건문. 중첩된 if-else를 평평하게 펴서 핵심 흐름을 드러낸다.

  • 커맨드 패턴

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

  • 임시 변수를 질의 함수로 바꾸기

    계산 결과를 담아 두던 임시 변수를 그 값을 돌려주는 함수로 바꾸는 리팩터링. 긴 함수를 쪼개기 전 단계로 특히 쓸모 있다.

  • 변수·컬렉션 캡슐화

    넓은 범위에서 쓰이는 데이터에 직접 접근하지 못하게 하고, 읽고 쓰는 함수를 통해서만 다루게 하는 리팩터링. 데이터를 옮기거나 바꿀 때 고쳐야 할 곳이 접근 함수 한 곳으로 좁아진다.

보기 옵션