본문 바로가기

Backend/디자인 패턴

Decorator Pattern (데코레이터 패턴)

데코레이터는 기존 객체의 동작을 동적으로 증강시키는 패턴입니다.

상속과의 차이점은 동일한 클래스의 모든 객체에 적용되지 않고 명시적으로 데코레이트한 인스턴스에만 추가되는 것 입니다.

데코레이트 라는 이름처럼 기존 객체를 추가적으로 좀 더 꾸며준다고 보면 쉬울 것 같습니다.

 

방식은 프록시 패턴과 비슷하게 볼 수 있지만, 객체의 기존 동작을 수정하는 대신 새로운 기능을 추가함으로써 기능을 증가시킵니다.

 

const decorate = (target) => {
  target.method2 = () => {
    return 'method2';
  };

  return target;
};

class Subject {
  method1() {
    return 'method1';
  }
}

const decoratedSubject = decorate(new Subject());

console.info(decoratedSubject.method1()); // "method1"
console.info(decoratedSubject.method2()); // "method2"

 

JS 에서 구현은 간단합니다. 프록시 패턴과 굳이 구분해야 할까 싶을정도로 비슷해 보이기도 하구요.

객체 지향의 코드에선, 이런식으로 구현하기 보단 적당한 상속과 객체 간의 합성을 통해 데코레이터 패턴을 구현합니다.

 

interface Character {
  attack(): void;
}

class Warrior implements Character {
  attack(): void {
    console.info('attack with sword');
  }
}

class Wizard implements Character {
  attack(): void {
    console.info('attack with magic');
  }
}

abstract class MergedCharacter implements Character {
  protected readonly character: Character;

  constructor(character: Character) {
    this.character = character;
  }

  attack(): void {
    this.character.attack();
  }
}

class AdvancedCharacter extends MergedCharacter {
  constructor(character: Character) {
    super(character);
  }

  attack(): void {
    super.attack();
    this.advancedAttack();
  }

  private advancedAttack(): void {
    console.info('advanced attack');
  }
}

const c1 = new Warrior();
c1.attack(); // attack with sword

const c2 = new Wizard();
c2.attack(); // attack with magic

const advancedC1 = new AdvancedCharacter(new Warrior());
advancedC1.attack(); // attack with sword advanced attack

 

뭔가 적당한 예시가 아닐 수도 있긴 합니다만..

전사, 마법사 라는 클래스가 있는 상태에서 각각 승급한 형태를 구현하려 할 때의 상황입니다.

Warrior, Wizard 를 상속하는 또다른 클래스를 만들 수도 있겠지만 승급에 의해 향상된 공격만 추가되는 정도의 요구사항이라면

AdvancedCahracter 를 만들어 기존 Character 와 합성하는 형태로도 작성이 가능하다는 것입니다.

 

물론 실제 게임이었다면 전사, 마법사라는 직업이 승급한 형태가 동일한 기능을 제공하진 않을테니 이런식의 구현이 쓰일 일이

오히려 없겠지만, 예시로 봐주시면 좋을 것 같습니다.

 

어느정도 예시를 통해 데코레이터 패턴의 장단점을 알 수 있게 되었습니다.

 

장점

 

  • 유연성
  • 기능 확장 시 상속 없이 가능
  • 런타임에서의 동적 기능 추가

 

단점

 

  • 코드가 복잡해질 수 있음
  • 객체가 오히려 더 늘어날 수 있음
  • 런타임에 추가된다는 건 디버깅의 난이도를 올릴 수 있음
  • 기존 데코레이터 패턴이 추구하려는 방향과 멀어질 수 있다..

 

단점에서 보이듯이 데코레이터 패턴을 적용하기 전 더 괜찮은 설계 방법은 없을지 고민하는게 좋을 것 같습니다.