Decorator Pattern
스펀지 케이크가 1개 있다고 가정!
크림만 바르면 아무 모양이 없는 크림케이크가 됨
여기에 딸기를 얹으면 딸기 케이크가 되고, 초콜릿을 장식하고 이름을 쓰고 초를 꽂으면 생일케이크가 됨
스펀지케이크도, 크림케이크도, 딸기케이크도 생일케이크도 처음에는 모두 다 같 은 스펀지 케이크지만 장식을 하면 목적에 맞는 케이크가 되는 것
객체도 이런 식으로 장식의 기능을 하나씩 추가하면 좀 더 목적에 맞는 객체가 됨
이러한 패턴을 데코레이터 디자인 패턴이라고 함

Component는 내용물
ConcreteComponent는 구체적인 내용물
Decorator 장식
Decorator pattern은 이름에서 알 수 있듯이 Object를 꾸며주는 역할

원하는 기능으로 감싸서 사용할 수 있게 만들어주는 패턴
class Animal: #Component
def speak(self):
pass
class Cat(Animal): #Concrete Component
def speak(self):
print("야옹", end="")
class Dog(Animal): #Concrete Component
def speak(self):
print("왈왈", end="")
def makeSpeak(animal:Animal):
animal.speak()
print(" ")
class Deco(Animal): #Decorator. Animal을 꾸밈. Animal과 동일한 interface
def __init__(self, animal:Animal):
self.animal = animal
def speak(self):
self.animal.speak()
class WthSmile(Deco): #Concrete Decorator
def speak(self):
self.animal.speak()
print(" :) ", end='')
class WthSad(Deco): # Concrete Decorator
def speak(self):
self.animal.speak()
print(" :( ", end='')
kitty = Cat()
makeSpeak(kitty)
smile_kitty = WthSmile(kitty)
makeSpeak(smile_kitty)
smile_sad_kitty = WthSad(smile_kitty)
makeSpeak(smile_sad_kitty)

기존의 Object에 더 많은 기능을 추가하기 위해 꾸미는 패턴이 필요할 때 사용됨
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(n):
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 #null
class Deco(Display):
def __init__(self, display:Display):
self.display = display
class SideBorder(Deco): #Concrete Decorator 1
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): #Concrete Decorator 1
def __init__(self, display):
super().__init__(display)
def getColumns(self):
return 1 + self.display.getColumns() + 1
def getRows(self):
return self.display.getRows()
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): #index = 개수
return "+" + self.makeLine("-", self.display.getColumns()) + "+"
else:
return "|" + self.display.getRowText(row-1)+"|"
b1 = StringDisplay("hello")
b2 = SideBorder(b1, "#")
b3 = FullBorder(b2)
b1.show()
b2.show()
b3.show()
a = StringDisplay("hello")
a = FullBorder(a)
a = SideBorder(a, "*")
a = FullBorder(a)
a = FullBorder(a)
a = SideBorder(a, "/")
a.show()
투과적 API
• Decorator 패턴에서는 Decorator와 Component를 동일시 하고 있음
• Decorator 클래스 (및 그 하위 클래스들)는 Component 클래스와 동일한 API를 가 짐
• 장식을 사용하여 내용물을 감싸도 API들이 감추어지지 않고 다른 클래스에서 볼 수 있 는데, 이 것을 API가 투과적이라고 함 -> 장식을 많이 사용해서 포함시켜도 API가 전혀 바뀌지 않음을 의미
• API가 투과적인 이유로 재귀적 구조가 등장함 -> decorator가 둘러싸고 있는 component들이 실제로는 다른 decorator가 되는 구조
• 양파껍질을 벗겨서 알맹이 인줄 알았으나, 그것 또한 껍질인 것과 같음
동적인 기능 추가
• Decorator 패턴에서 사용하는 “위임”은 Loose Coupling (느슨한 결합)이 됨
-> Tight Coupling (강한결합)은 클래스와 객체가 서로 의존하고 있는 상태, 일반적으로 유연성과 코드의 재사용성이 줄어들어 좋지 않음
• 따라서, 프레임워크의 소스를 변경하지 않고, 오브젝트의 관계를 변경한 새로운 오브젝트를 만들 수 있음