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