데코레이터 패턴(Decorator Pattern)
디자인패턴 공부하기
  • JavaScript

데코레이터 패턴(Decorator Pattern)

객체에 추가적인 요소를 동적으로 추가한다. 데코레이터는 서브클래스를 만드는 것을 통해 기능을 유연하게 확장할 수 있는 패턴이다.

데코레이터 패턴을 설명할 때 가장 많은 예로 등장하는 카페 주문에 대해 살펴본다.

아메리카노를 주문하고 여러가지 재료를 첨가하는 상황이다.
우유추가
샷추가
크림추가

class Beverage {
  cost() {
    let total = 0;

    if (hasMilk()) total += 300;
    if (hasShot()) total += 500;
    if (hasCream()) total += 300;
    if (hasCookie()) total += 1000;
  };
}
// 그리고 커피 클래스 에서는
class CaffeLatte extends Beverage {
  constructor() {
    super();
  }

  cost() {
    return super.cost() + 5000;
  };
}

이와같은 모습의 코드가 될것이다.
잘 만들었다고 생각할 수 있지만, 큰 문제점이 있다.

  1. 데코의 종류가 추가될 때마다 슈퍼클래스를 계속 수정해야한다.
  2. 동일한 데코를 여러번 추가하지 못한다.
디자인원칙 OCP : 클래스는 확장자에 대해서는 열려있어야 하지만 코드 변경에 대해서는 닫혀있어야 한다. 자세히보기

즉 기존 코드는 건드리지 말고 확장을 통해서 새로운 행동을 간단하게 추가할 수 있어야 한다.

아래의 코드를 살펴보자

class Beverage {
  let cost;
}

// 위 클래스는  건드리지 않습니다.

class DecoratorBeverage extends Beverage {
  cost() {
    throw 'You must be override this function!';
  }
}

class Americano extends DecoratorBeverage {
  cost() {
    return 4000;
  }
}

class CaffeLatte extends DecoratorBeverage {
  cost() {
    return 5000;
  }
}

이번은 데코 클래스를 만들어줍니다

class Cream extends DecoratorBeverage {
  constructor(beverage) {
    this.beverage = beverage;
  }

  cost() {
    return this.beverage.cst() + 300;
  }
}

class Shot extends DecoratorBeverage {
  constructor(beverage) {
    this.beverage = beverage;
  }

  cost() {
    return this.beverage.cst() + 500;
  }
}

class Milk extends DecoratorBeverage {
  constructor(beverage) {
    this.beverage = beverage;
  }

  cost() {
    return this.beverage.cst() + 300;
  }
}

마지막으로 다음과 같이 사용한다.

const americano = new Americano();
americano = new Shot(americano);
americano = new Milk(americano);

console.log('cost : ', americano.cost()); // 4800