데코레이터 패턴

3 minute read

데코레이터 패턴에서는 객체에 추가적인 요건을 동적으로 추가한다. 데코레이터는 서브클래스를 만들어서 기능을 유연하게 확장할 수 있는 방법을 제공한다.

클래스 다이어그램

decorator-pattern

각 데코레이터 안에는 구성요소에 대한 레퍼런스가 들어있는 인스턴스 변수가 있다.

데코레이터가 동적으로 구성요소에 새로운 상태나 행동을 추가할 수 있다.

특징

  • 상속을 통한 확장보다 유연하다.
  • 기존 코드를 수정하지 않고도 행동을 확장할 수 있다.
  • 구성요소를 감싸는 데코레이터의 개수에는 제한이 없다.
  • 다만, 자잘한 객체들이 너무 많이 추가되어, 그런 경우 코드가 복잡해질 수 있다. (이 경우, 팩토리와 빌더 패턴으로 해결할 수 있다.)

구현

첨가물을 추가하기 쉽고 자동으로 첨가물 가격이 추가되는 음료 클래스를 만들려한다.

음료를 나타내는 추상 클래스

abstract class Beverage {
  description = "없음";

  getDescription(): string {
    return this.description;
  }

  abstract cost(): number;
}

첨가물을 나타내는 추상 클래스

abstract class CondimentDecorator extends Beverage {
  abstract getDescription(): string;
}

음료 코드 구현

아메리카노와 하우스 블랜드 커피를 구현한다.

class Americano extends Beverage {
  constructor() {
    super();
    this.description = "아메리카노";
  }

  cost(): number {
    return 3800;
  }
}

class HouseBlend extends Beverage {
  constructor() {
    super();
    this.description = "하우스 블렌드 커피";
  }

  cost(): number {
    return 4600;
  }
}

첨가물 코드 구현

모카와 휘핑크림, 얼음을 구현한다.

// 모카
class Mocha extends CondimentDecorator {
  beverage: Beverage;

  constructor(beverage: Beverage) {
    super();
    this.beverage = beverage;
  }

  getDescription(): string {
    return `모카 ${this.beverage.getDescription()}`;
  }

  cost(): number {
    return 1000 + this.beverage.cost();
  }
}

// 휘핑 크림
class Whip extends CondimentDecorator {
  beverage: Beverage;

  constructor(beverage: Beverage) {
    super();
    this.beverage = beverage;
  }

  getDescription(): string {
    return `휘핑크림 ${this.beverage.getDescription()}`;
  }

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

// 얼음
class Ice extends CondimentDecorator {
  beverage: Beverage;

  constructor(beverage: Beverage) {
    super();
    this.beverage = beverage;
  }

  getDescription(): string {
    return `아이스 ${this.beverage.getDescription()}`;
  }

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

테스트

// 아이스 아메리카노
const americano = new Americano();
const icedAmericano = new Ice(americano);
console.log(icedAmericano.getDescription(), "", icedAmericano.cost());

// 모카 2개와 휘핑크림 올린 당 폭탄 하우스 블렌드
let sugarBomb = new HouseBlend();
sugarBomb = new Mocha(sugarBomb);
sugarBomb = new Mocha(sugarBomb);
sugarBomb = new Whip(sugarBomb);
console.log(sugarBomb.getDescription(), "", sugarBomb.cost());

결과

음료에 첨가물을 추가하기 쉽고, 추가된 첨가물에 따라 음료 가격이 자동으로 계산된다.

아이스 아메리카노 ₩ 4300
휘핑 크림 모카 모카 하우스 블렌드 커피 ₩ 6900