반응형

 
데코레이터 패턴이란 구조 패턴중의 하나로 상속(inheritance)과 합성(Composition)을 통해
기존 객체에 동적으로 동작을 추가하는 구조적인 패턴입니다.
 

새로운 서비스의 추가가 필요할때 일반적으로 상속을 이용합니다.

하지만 상속을 사용한다면 다음과 같은 문제점이 발생할 수 있습니다.

 

1. 나중 추가사항이 생긴다면 superclass를 수정하고 기능을 집어넣어야 합니다.

 

2. 1번으로 인하여 superclass는 비대해지고

SOLID의 SRP를 위반합니다. (어떤 특정 subClass A에서만 사용하는 메소드가 B, C에도 상속됩니다.)

 

그래서 OCP의 컨셉을 활용하여 해결하려고 시도하는 디자인 패턴입니다.

 

예를 들어 라면집을 하기 위해서 라면을 구현한다고 해봅시다.
라면엔 면과 다시마가 들어간다고 볼 수 있습니다.

 

class Food {
  add() {
    //재료 추가
  }
}

class Ramen extends Food {
  addKelp() {}

  addNoodle() {}

  add() {
    this.addNoodle();
    this.addKelp();
  }
}

 

그런데 라면의 인기가 높아져서 라면에 온갖 고추장, 치즈, 짜장, 곰탕등 소스를 집어넣기 시작했습니다.

그리고 각 소스를 그냥 넣는것이 아니라, 1개 이상 중복해서 넣어서 섞어먹는 이상한 사람들이 등장하기 시작했습니다.

 

이때 상속으로 구현하게 된다면 중복으로 쓰이는 메소드들은 상위 클래스인 Ramen class 혹은 어딘가에 구현이 되고, 상
속을 시켜야할것입니다.
예를 들어 치즈짜장라면과 치즈곰탕라면은 addCheese라는 공통 함수를 Ramen Class에 넣거나 CheeseRamen Class에 넣어야할것인데 그렇다면
 
1. 공통 class인 Ramen에 쓰이면 치즈를 안넣는 class도 상속을 받아서 문제가 발생
2. CheeseRamen Class에 넣는다면 조합에 따라 만들어지는 class의 개수가 기하급수적으로 증가하게 될것

 

입니다. 

 

그래서 데코레이터를 사용해서 치즈짜장라면과 치즈곰탕라면을..

상속을 사용하지 않고 데코레이터를 사용해서 구현해봅시다...

(글 쓰면서 뭔가 내용이 산으로 가는 느낌인데..?)

 

class Food {
  add() {
    //재료 추가
  }
}

class Ramen extends Food {
  addKelp() {
    return 'add Kelp ';
  }

  addNoodle() {
    return 'add noodle ';
  }

  assemble() {
    return this.addNoodle() + this.addKelp();
  }
}

class RamenDecorator extends Food {
  constructor(Ramen) {
    super();
    this.Ramen = Ramen;
  }

  assemble() {
    return this.Ramen.assemble();
  }
}

 

우선 Base가 되는 Ramen과 Ramen Decorator입니다.

RamenDecorator는 만들고 싶은 Ramen을 주입받아 조립해 사용합니다.

 

치즈라면을 일단 만들어 봅시다.

 

class CheeseRamenDecorator extends RamenDecorator {
  addCheese() {
    return 'add cheese';
  }

  assemble() {
    return super.assemble() + this.addCheese();
  }
}


const CheeseRamen = new CheeseRamenDecorator(new Ramen());
console.log(CheeseRamen.assemble());

 

그림으로 보면 이런 상태입니다.

$ node test.js
add noodle add Kelp add cheese

코드를 실제로 돌려보면 잘 적용된걸 확인할 수 있습니다.

 

이제 아까 말했던 치즈짜장라면과 치즈곰탕라면을 구현해봅시다.
class BlackbeanSauceRamenDecorator extends RamenDecorator {
  addBlackbeanSauce() {
    return 'add BlackbeanSauce';
  }

  assemble() {
    return super.assemble() + this.addBlackbeanSauce();
  }
}

class BeefBoneSoupRamenDecorator extends RamenDecorator {
  addBeefBoneSoup() {
    return 'add BeefBoneSoup';
  }

  assemble() {
    return super.assemble() + this.addBeefBoneSoup();
  }
}


const CheeseRamen = new CheeseRamenDecorator(new Ramen());
console.log(CheeseRamen.assemble());

const CheeseBlackbeanSauceRamen = new BlackbeanSauceRamenDecorator(
  new CheeseRamenDecorator(new Ramen()),
);
const CheeseBeefBoneSoupRamen = new BeefBoneSoupRamenDecorator(
  new CheeseRamenDecorator(new Ramen()),
);

console.log(CheeseBlackbeanSauceRamen.assemble());
console.log(CheeseBeefBoneSoupRamen.assemble());

아.. 라면 이름 예시를 잘못들었는데 지금 돌이킬순 없는거 같아요..

 

그림으로 보면 이런상태입니다. 

 

add noodle add Kelp add cheeseadd BlackbeanSauce
add noodle add Kelp add cheeseadd BeefBoneSoup

코드를 실제로 돌려보면 잘 적용된걸 확인할 수 있습니다.

그리고 상속으로 구현했을때보다 훨씬 깨끗해진걸 볼 수 있습니다.

 

지금은 조합 예시가 2~3개이지만 프로그램이 발전되며 조합이 수십~수백가지로 늘어났을때에도 상속보다 유연하게 처리 가능해진걸 확인할 수 있었습니다.

 

 

tmi 하나 하자면.. K사 기술면접에서 super class에 공통 함수를 놔둔다면 안쓰이는 subclass에서는 이것을 어떻게 처리해야할것인가라는 질문이 나왔었는데 그 당시에는 답변을 못했다가 오늘 알게됬네요 ㅠㅠ

 

reference 

 

https://gmlwjd9405.github.io/2018/07/09/decorator-pattern.html

 

[Design Pattern] 데코레이터 패턴이란 - Heee's Development Blog

Step by step goes a long way.

gmlwjd9405.github.io

 

https://steady-coding.tistory.com/391

 

[디자인 패턴] 데코레이터(Decorater) 패턴이란?

안녕하세요? 제이온입니다. 저번 시간에는 상태 패턴에 대해서 알아 보았습니다. 오늘은 데코레이터 패턴을 설명하겠습니다. 데코레이터(Decorater) 패턴 데코레이터 패턴은 객체에 추가적인 요건

steady-coding.tistory.com

 

반응형

+ Recent posts