A A
[Design Pattern] Decorator Pattern - ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด

Decorator Pattern

๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด(Decorator Pattern)์€ ๊ฐ์ฒด์˜ ๊ธฐ๋Šฅ์„ ๋™์ ์œผ๋กœ ์ถ”๊ฐ€ํ•˜๊ณ  ํ™•์žฅํ•  ์ˆ˜ ์žˆ๋Š” ํŒจํ„ด์ž…๋‹ˆ๋‹ค.
  • ์ด ํŒจํ„ด์€ ์ƒ์†์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ ๋„ ๊ฐ์ฒด์— ์ƒˆ๋กœ์šด ํ–‰๋™์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.
  • ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด์€ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ๊ฐ์ฒด๋ฅผ ์กฐํ•ฉํ•˜์—ฌ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ๋™์ ์œผ๋กœ ์กฐํ•ฉํ•  ์ˆ˜ ์žˆ๋Š” ์œ ์—ฐ์„ฑ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

  • Decorator Pattern์€ Object(๊ฐ์ฒด)๋ฅผ ๊พธ๋ฉฐ์ฃผ๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
    • ์›ํ•˜๋Š” ๊ธฐ๋Šฅ์œผ๋กœ ๊ฐ์‹ธ์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ค์–ด์ฃผ๋Š” ํŒจํ„ด ์ž…๋‹ˆ๋‹ค.

class Animal:
    def speak(self):
        pass

class Cat(Animal):
    def speak(self):
        print("Meow", end='')

class Dog(Animal):
    def speak(self):
        print("Bark", end='')
  • Animal, Cat, Dog Class๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Cat, Dog๋Š” ๋ชจ๋‘ Animal์„ ์ƒ์† ๋ฐ›์Šต๋‹ˆ๋‹ค.
  • ์„ธ Class ๋ชจ๋‘ Speak ํ•จ์ˆ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
class makeSpace(animal:Animal):
    animal.speak()
    print(" ")
  • makeSpeak ํ•จ์ˆ˜๋Š” argument๋กœ animal์„ ๋ฐ›๊ณ  ํ•จ์ˆ˜ ์•ˆ์—์„œ Animal์˜ speak๋ฅผ ํ˜ธ์ถœ ํ•ฉ๋‹ˆ๋‹ค.

  • ์—ฌ๊ธฐ์„œ Cat Object์ธ Kitty๋ฅผ makeSpeak ํ•จ์ˆ˜๋กœ ํ˜ธ์ถœํ•˜๋ฉด meow ๋ผ๋Š” ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ต๋‹ˆ๋‹ค.

class Deco(Animal):
    def __init__(self, animal:Animal):
        self.animals = animal
    def speak(self):
        self.animals.speak()
  • Deco class๋Š” Animal์„ ๊พธ๋ฏธ๋Š” ์—ญํ• ๋กœ Animal๊ณผ ๋™์ผํ•œ interface๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
  • Animal์—์„œ ์ƒ์†๋ฐ›๊ณ  constructor์—์„œ Animal์„ property๋กœ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ  speak๋Š” Animal์˜ speak ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
class WthSmile(Deco):
    def speak(self):
        self.animal.speak()
        print(":)", end='')

class WthHeartEyes(Deco):
    def speak(self):
        self.animal.speak()
        print("<3", end='')
  • WithSmile๊ณผ WithHeartEyes๋Š” Deco๋ฅผ ์ƒ์†๋ฐ›๊ณ  ๋ถ€๋ชจ์ธ Deco ๋‚ด๋ถ€์— ์žˆ๋Š” Animal์—์„œ speak๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ๊ฐ๊ฐ :), <3์„ printํ•ฉ๋‹ˆ๋‹ค.
Decorator Pattern์€ ๊ธฐ์กด์˜ Object์— ๋” ๋งŽ์€ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๊ธฐ ์œ„ํ•ด ๊พธ๋ฏธ๋Š” ํŒจํ„ด์ด ํ•„์š”ํ• ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

Example (Decorator Pattern)

                +----------------+
                |    Display     |
                +----------------+
                | + getColumns() |
                | + getRows()    |
                | + getRowText() |
                +----------------+
                       ^
                       |
      +----------------+----------------+
      |                                 |
+---------------+                   +-----------------+
| StringDisplay |                   | Deco (Decorator)|
+---------------+                   +-----------------+
| - string      |                   | - display       |
| + methods     |                   | + methods       |
+---------------+                   +-----------------+
                                              ^
                                              |
                 +----------------+-----------------+
                 |                                  |
         +-------------+                      +-------------+
         |  SideBorder |                      |  FullBorder |
         +-------------+                      +-------------+
         | - deco      |                      | + methods   |
         | + methods   |                      +-------------+
         +-------------+
class Display:
    def getColumns(self):
        pass

    def getRows(self):
        pass

    def getRowText(self, row):
        pass

    def show(self):
        n = self.getRows()
        for i in range(self.getRows()):
            print(self.getRowText(i))

class StringDisplay(Display):
    def __init__(self, letters):
        self.letters = letters

    def getColumns(self):
        return len(self.letters)

    def getRows(self):
        return 1

    def getRowText(self, row):
        if row == 0:
            return self.letters
        else:
            return -1

class Deco(Display):
    def __init__(self, display: Display):
        self.display = display

class SideBorder(Deco):
    def __init__(self, display, deco):
        super().__init__(display)
        self.borderDeco = deco

    def getColumns(self):
        return 1 + self.display.getColumns() + 1

    def getRows(self):
        return self.display.getRows()

    def getRowText(self, row):
        return self.borderDeco + self.display.getRowText(row) + self.borderDeco

class FullBorder(Deco):
    def __init__(self, display):
        super().__init__(display)

    def getColumns(self):
        return 1 + self.display.getColumns() + 1

    def getRows(self):
        return 1 + self.display.getRows() + 1

    def makeLine(self, deco, count):
        buffer = ""
        for i in range(count):
            buffer += deco
        return buffer

    def getRowText(self, row):
        if row == 0:
            return "+" + self.makeLine("-", self.display.getColumns()) + "+"
        elif row == (self.display.getRows() + 1):
            return "+" + self.makeLine("-", self.display.getColumns()) + "+"
        else:
            return "|" + self.display.getRowText(row - 1) + "|"
  • Client Example 1
# Example usage
b1 = StringDisplay("hello")
b2 = SideBorder(b1, "#")
b3 = FullBorder(b2)
b1.show()
b2.show()
b3.show()
  • Client Example Output 1
hello
#hello#
+-------+
|#hello#|
+-------+
  • Client Example 2
b1 = StringDisplay("hello, world")
b1 = FullBorder(b1)
b1.show()
  • Client Example Output 2
+------------+
|hello, world|
+------------+
  • Client Example 3
a = StringDisplay("hello")
a = FullBorder(a)
a = SideBorder(a, "*")
a = FullBorder(a)
a = FullBorder(a)
a = SideBorder(a, "/")
a.show()
  • Client Example Output 3
/+-----------+/
/|+---------+|/
/||*+-----+*||/
/||*|hello|*||/
/||*+-----+*||/
/|+---------+|/
/+-----------+/
  • Client Example 4
b4 = SideBorder(
    FullBorder(
        FullBorder(
            SideBorder(
                FullBorder(
                    StringDisplay("hello")
                ), "*"
            ), 
        ), 
    "/"
    )
)
b4.show()
  • Client Example Output 4
/+-----------+/
/|+---------+|/
/||*+-----+*||/
/||*|hello|*||/
/||*+-----+*||/
/|+---------+|/
/+-----------+/

ํˆฌ๊ณผ์  API

Decorator ํŒจํ„ด์—์„œ๋Š” Decorator์™€ Component๋ฅผ ๋™์‹œ์— ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
  • Decorator ํด๋ž˜์Šค (๋ฐ ๊ทธ ํ•˜์œ„ ํด๋ž˜์Šค๋“ค)๋Š” Component ํด๋ž˜์Šค์™€ ๋™์ผํ•œ API๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค.
  • ์žฅ์‹์„ ์‚ฌ์šฉํ•˜์—ฌ ๋‚ด์šฉ์„ ๊ฐ์‹ธ๋„ API๋“ค์ด ๊ฐ์ถ”์–ด์ง€์ง€ ์•Š๊ณ  ๋‹ค๋ฅธ ํด๋ž˜์Šค์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด๊ฒƒ์„ API๊ฐ€ ํˆฌ๊ณผ์ ์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.
    • ์žฅ์‹์„ ๋งŽ์ด ์‚ฌ์šฉํ•ด์„œ ํฌํ•จ์‹œ์ผœ๋„ API๊ฐ€ ์ „ํ˜€ ๋ฐ”๋€Œ์ง€ ์•Š์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
  • API๊ฐ€ ํˆฌ๊ณผ์ ์ธ ์ด์œ ๋กœ ์žฌ๊ท€์  ๊ตฌ์กฐ๊ฐ€ ๋“ฑ์žฅํ–ˆ์Šต๋‹ˆ๋‹ค.
    • Decorator๊ฐ€ ๋‘˜๋Ÿฌ์‹ธ๊ณ  ์žˆ๋Š” component๋“ค์ด ์‹ค์ œ๋กœ๋Š” ๋‹ค๋ฅธ decorator๊ฐ€ ๋˜๋Š” ๊ตฌ์กฐ ์ž…๋‹ˆ๋‹ค.
  • ์–‘ํŒŒ๊ป์งˆ์„ ๋ฒ—๊ฒจ์„œ ์•Œ๋งน์ด ์ธ์ค„ ์•Œ์•˜์œผ๋‚˜, ๊ทธ๊ฒƒ ๋˜ํ•œ ๊ป์งˆ์ธ ๊ฒƒ๊ณผ ๊ฐ™์€ ๋ง์ž…๋‹ˆ๋‹ค.

๋™์ ์ธ ๊ธฐ๋Šฅ ์ถ”๊ฐ€

  • Decorator ํŒจํ„ด์—์„œ ์‚ฌ์šฉํ•˜๋Š” "์œ„์ž„"์€ Loose Coupling (๋Š์Šจํ•œ ๊ฒฐํ•ฉ)์ด ๋ฉ๋‹ˆ๋‹ค.
    • Tight Coupling (๊ฐ•ํ•œ ๊ฒฐํ•ฉ)์€ ํด๋ž˜์Šค์™€ ๊ฐ์ฒด๊ฐ€ ์„œ๋กœ ์˜์กดํ•˜๊ณ  ์žˆ๋Š” ์ƒํƒœ์ž…๋‹ˆ๋‹ค.
    • ์ด ์ƒํƒœ์—์„œ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์œ ์—ฐ์„ฑ๊ณผ ์ฝ”๋“œ์˜ ์žฌ์‚ฌ์šฉ์„ฑ์ด ์ค„์–ด๋“ค์–ด ์ข‹์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ๋”ฐ๋ผ์„œ, ํ”„๋ ˆ์ž„์›Œํฌ์˜ ์†Œ์Šค๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ , ์˜ค๋ธŒ์ ํŠธ์˜ ๊ด€๊ณ„๋ฅผ ๋ณ€๊ฒฝํ•œ ์ƒˆ๋กœ์šด ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.