Contents

Decorator Design Pattern

Decorator Pattern

๊ฐ์ฒด๊ฐ€ ํ•  ์ˆ˜ ์žˆ๋Š” ํ–‰๋™๋“ค์ด ์ •์˜๋œ ์ƒํƒœ์—์„œ, ๊ฐ์ฒด์— ์ถ”๊ฐ€์ ์ธ ํ–‰๋™์ด ์š”๊ตฌ๋˜์—ˆ์„ ๋•Œ ๊ฐ์ฒด๋ฅผ ํ™•์žฅํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ ํŠน์ˆ˜ํ•œ ํ–‰๋™์„ ํ•˜๋Š” ๊ฐ์ฒด ๋ž˜ํผ์— ๋„ฃ์–ด์„œ ์ถ”๊ฐ€์ ์ธ ํ–‰๋™์„ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค. ์ฆ‰, ๊ธฐ๋ณธ๊ธฐ๋Šฅ์— ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์˜ ์ข…๋ฅ˜๊ฐ€ ๋งŽ์€ ๊ฒฝ์šฐ ๊ฐ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ์„ Decorator์— ์ •์˜ํ•˜์—ฌ ์กฐํ•ฉํ•จ์œผ๋กœ์จ ์ถ”๊ฐ€๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ํ‘ธ์‰ฌ ์•Œ๋ฆผ์„ ๋ณด๋‚ผ ๋•Œ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋Š” ์ดˆ๊ธฐ์— ๊ธฐํš๋œ ๋ฐ์ดํ„ฐ๋ณด๋‹ค ๋Š˜์–ด๋‚  ์ˆ˜๊ฐ€ ์žˆ๋‹ค. A,B,C ํ‘ธ์‰ฌ๋ฅผ ๋ณด๋‚ผ ๋•Œ ๊ธฐ๋ณธ์ ์ธ ํ‹€์€ ๋น„์Šทํ•˜์ง€๋งŒ, ๊ฐ๊ฐ ๋ณด๋‚ด์•ผ ํ•  ๋ฐ์ดํ„ฐ๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ ์กฐ๊ธˆ์”ฉ ๋‹ค๋ฅด๊ธฐ ๋งˆ๋ จ์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์–ด๋–ค ํ‘ธ์‰ฌ๋Š” app scheme์„ ๋ณด๋‚ด์•ผํ•˜๊ณ , ์–ด๋–ค ํ‘ธ์‰ฌ๋Š” webview๋ฅผ ๋„์šธ url์„ ๋ณด๋‚ด์•ผํ•˜๋Š” ๊ทธ๋Ÿฐ ๊ฒฝ์šฐ์ด๋‹ค. ์ด๋ ‡๊ฒŒ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ •ํ•ด์ง„ ํ‹€์€ ์กด์žฌํ•˜์ง€๋งŒ, ์ถ”๊ฐ€์ ์ธ ๊ธฐ๋Šฅ์ด ๋งŽ์•„์ง€๊ณ  ์ด๋ฅผ ์กฐํ•ฉํ•ด์•ผ ํ•  ๊ฒฝ์šฐ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด์„ ์“ฐ๋ฉด ๋™์ ์œผ๋กœ ์—ฌ๋Ÿฌ ์กฐํ•ฉ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฌผ๋ก , ์ƒ์†์„ ์ด์šฉํ•ด์„œ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ, ์กฐํ•ฉ์„ ๊ฐ€์ง„ ํด๋ž˜์Šค๋“ค์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋‹ค๋งŒ ์ƒ์†์€ ์ •์ ์ด๋‹ค. ํ•œ๋ฒˆ ๊ตฌํ˜„๋˜๋ฉด ๋Ÿฐํƒ€์ž„์— ํ–‰๋™์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋‹ค.

๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด์—์„œ๋Š” ์ด๋Ÿฌํ•œ ์‚ฌํ•ญ์„ ๊ทน๋ณตํ•˜๊ธฐ ์œ„ํ•ด, ์ƒ์† ๋Œ€์‹  ์ง‘ํ•ฉ ๊ด€๊ณ„ ๋˜๋Š” ๊ตฌ์„ฑ์„ ํ™œ์šฉํ•œ๋‹ค. ์ด๋Ÿฌํ•œ ์ ‘๊ทผ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ฉด ์—ฐ๊ฒฐ๋œ ๋„์šฐ๋ฏธ ๊ฐ์ฒด๋ฅผ ๋‹ค๋ฅธ ๊ฐ์ฒด๋กœ ์‰ฝ๊ฒŒ ๋Œ€์ฒดํ•˜์—ฌ ๋Ÿฐํƒ€์ž„ ๋•Œ์˜ ์ปจํ…Œ์ด๋„ˆ์˜ ํ–‰๋™์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ฐ์ฒด๋Š” ์—ฌ๋Ÿฌ ํด๋ž˜์Šค์˜ ํ–‰๋™์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ณ , ์—ฌ๋Ÿฌ ๊ฐ์ฒด์— ๋Œ€ํ•œ ์ฐธ์กฐ๊ฐ€ ์žˆ์œผ๋ฉฐ ์ด ๊ฐ์ฒด๋“ค์— ๋ชจ๋“  ์ข…๋ฅ˜์˜ ์ž‘์—…์„ ์œ„์ž„ํ•œ๋‹ค.

์ด๋ ‡๊ฒŒ ๊ตฌ์„ฑํ•˜๊ณ  ๋‚˜์„œ email -> facebook -> slack์œผ๋กœ ์•Œ๋ฆผ์„ ๋ณด๋‚ด์•ผ ํ•œ๋‹ค๋ฉด ํด๋ผ์ด์–ธํŠธ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌ์„ฑํ•˜๋ฉด ๋œ๋‹ค.

1
2
3
4
5
6
7
8
stack = new Notifier();
if (facebookEnabled) {
  stack = new FacebookDecorator(stack);
}
if (slackEnabled) {
  stack = new SlackDecorator(stack);
}
app.setNotifier(stack);

๊ตฌ์กฐ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/**
 * Base Component interface ๋Š” ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์— ์˜ํ•ด ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๋Š” ํ–‰๋™์„ ์ •์˜ํ•œ๋‹ค.
 */
interface component {
  operation(): string;
}

/**
 * Concrete Component ๋Š” Component interface์˜ ๊ธฐ๋ณธ ํ–‰๋™์„ ์ •์˜ํ•œ๋‹ค.
 * ํ•ด๋‹น ํด๋ž˜์Šค๋Š” ๋‹ค์–‘ํ•œ ๋ณ€ํ˜•์ด ์ผ์–ด๋‚  ์ˆ˜ ์žˆ๋‹ค.
 */
class ConcreteComponent implements Component {
  public operation(): string {
    return "ConcreteComponent";
  }
}

/**
 * The base Decorator class follows the same interface as the other components.
 * The primary purpose of this class is to define the wrapping interface for all
 * concrete decorators. The default implementation of the wrapping code might
 * include a field for storing a wrapped component and the means to initialize
 * it.
 */
class Decorator implements Component {
  protected component: Component;

  constructor(component: Component) {
    this.component = component;
  }

  /**
   * The Decorator delegates all work to the wrapped component.
   */
  public operation(): string {
    return this.component.operation();
  }
}

/**
 * Concrete Decorators call the wrapped object and alter its result in some way.
 */
class ConcreteDecoratorA extends Decorator {
  /**
   * Decorators may call parent implementation of the operation, instead of
   * calling the wrapped object directly. This approach simplifies extension
   * of decorator classes.
   */
  public operation(): string {
    return `ConcreteDecoratorA(${super.operation()})`;
  }
}

/**
 * Decorators can execute their behavior either before or after the call to a
 * wrapped object.
 */
class ConcreteDecoratorB extends Decorator {
  public operation(): string {
    return `ConcreteDecoratorB(${super.operation()})`;
  }
}

/**
 * The client code works with all objects using the Component interface. This
 * way it can stay independent of the concrete classes of components it works
 * with.
 */
function clientCode(component: Component) {
  // ...

  console.log(`RESULT: ${component.operation()}`);

  // ...
}

/**
 * This way the client code can support both simple components...
 */
const simple = new ConcreteComponent();
console.log("Client: I've got a simple component:");
clientCode(simple);
console.log("");

/**
 * ...as well as decorated ones.
 *
 * Note how decorators can wrap not only simple components but the other
 * decorators as well.
 */
const decorator1 = new ConcreteDecoratorA(simple);
const decorator2 = new ConcreteDecoratorB(decorator1);
console.log("Client: Now I've got a decorated component:");
clientCode(decorator2);

์ ์šฉ

์ƒ์†์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ์ฒด์˜ ํ–‰๋™์„ ํ™•์žฅํ•˜๋Š” ๊ฒƒ์ด ์–ด์ƒ‰ํ•˜๊ฑฐ๋‚˜ ๋ถˆ๊ฐ€๋Šฅํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ํ•„์š”๋กœ ํ•˜๋Š” ์ถ”๊ฐ€๊ธฐ๋Šฅ์ด ํ•œ๊ฐœ์”ฉ ๋Š˜์–ด๊ฐˆ ๋•Œ๋งˆ๋‹ค 2๋ฐฐ๋กœ ์ƒ์†ํด๋ž˜์Šค๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค๊ฑฐ๋‚˜ ๋ถˆํ•„์š”ํ•œ ์ƒ์†์„ ํ•  ๋•Œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๋ฉด ์ฝ”๋“œ๊ฐ€ ๊น”๋”ํ•ด์ง„๋‹ค.

์–ด๋–ค ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์œผ๋ฉด์„œ ์ถ”๊ฐ€์ ์ธ ํ–‰๋™์„ ๊ฐ์ฒด์— ํ• ๋‹นํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.