✏️10.4 조건부 로직을 다형성으로 바꾸기 (Replace Conditional with Polymorphism)

리팩터링 전

switch (bird.type) {
  case '유럽 제비':
    return "보통이다";
  case '아프리카 제비':
    return (bird.numberOfCoconuts > 2) ? "지쳤다" : "보통이다";
  case 'NorwegianBlueParrot':
    return (bird.voltage > 100) ? "그을렸다" : "예쁘다";
  default:
    return "알 수 없다";

리팩터링 후

class EuropeanSwallow {
  get plumage() {
    return "보통이다";
  }
  ...
class AfricanSwallow {
  get plumage() {
     return (this.numberOfCoconuts > 2) ? "지쳤다" : "보통이다";
  }
  ...
class NorwegianBlueParrot {
  get plumage() {
     return (this.voltage > 100) ? "그을렸다" : "예쁘다";
  }

🧷 배경

조건문을 클래스와 다형성을 이용하면 더 확실하게 분리할 수 있다.

기본 로직을 슈퍼클래스로 넣어서 서브클래스의 변형 동작에 신경 쓰지 않고 기본에 집중하게 할 수 있게 한다.

🧷 절차

  1. 다형적 동작을 표현하는 클래스들이 아직 없다면 만들고 팩터리 함수도 함께 만든다.

  2. 호출하는 코드에서 팩터리 함수를 사용하게 한다.

  3. 조건부 로직 함수를 슈퍼클래스로 옮긴다.

  4. 서브클래스 중 하나를 선택하여, 슈퍼클래스의 조건부 로직 메서드를 오버라이드한다.

  5. 같은 방식으로 각 조건절을 해당 서브클래스에서 메서드로 구현한다.

  6. 슈퍼클래스 메서드에는 기본 동작 부분만 남긴다.

🧷 예시

🧷 리팩터링 전

function plumages(birds) {
  return new Map(birds.map((b) => [b.name, plumage(b)]));
}

function speeds(birds) {
  return new Map(birds.map((b) => [b.name, airSpeedVelocity(b)]));
}

function plumage(bird) {
  switch (bird.type) {
    case '유럽 제비':
      return '보통이다';
    case '아프리카 제비':
      return bird.numberOfCoconuts > 2 ? '지쳤다' : '보통이다';
    default:
      return '알 수 없다';
  }
}

function airSpeedVelocity(bird) {
  switch (bird.type) {
    case '유럽 제비':
      return 35;
    case '아프리카 제비':
      return 40 - 2 * bird.numberOfCoconuts;
    default:
      return null;
  }
}

🧷 리팩터링 후

function plumages(birds) {
  return new Map(birds
                .map((b) => createBird(b))
                .map((bird) => [bird.name, bird.plumage]));
}

function speeds(birds) {
  return new Map(birds
                .map((b) => createBird(b))
                .map((bird) => [bird.name, bird.airSpeedVelocity]));
}

class Bird {
  constructor(birdObject) {
    Object.assign(this, birdObject);
  }

  get plumage() {
    return '알 수 없다';
  }

  get airSpeedVelocity() {
    return null;
  }
}

class EuropeanSwallow extends Bird {
  get plumage() {
    return '보통이다';
  }

  get airSpeedVelocity() {
    return 35;
  }
}

class AfricanSwallow extends Bird {
  get plumage() {
    return this.numberOfCoconuts > 2 ? '지쳤다' : '보통이다';
  }

  get airSpeedVelocity() {
    return 40 - 2 * this.numberOfCoconuts;
  }
}

function createBird(bird) {
  switch (this.type) {
    case '유럽 제비':
      return new EuropeanSwallow(bird);
    case '아프리카 제비':
      return new AfricanSwallow(bird);
    default:
      return new Bird(bird);
  }
}

Last updated