Decorator Pattern을 알아보았다. Decorator 정의를 살펴보자.
Decorator is a structural pattern that allows adding new behaviors to objects dynamically by placing them inside special wrappper objects.
Decorator 패턴은 알맹이 객체를 감싸서 우리가 원하는 '추가적인 행동'을 더하는 패턴이다.
패턴을 알아보면서 Christopher Okhravi 영상이 패턴에 대해서 설명이 잘되어있어 영상을 보면서 익힌 내용을 정리해 보았다.
카페에서 의뢰를 받았다. 음료의 설명과 가격을 표시하는 장치를 만들어 달라고 한다.
음료 to class
음료는 추상클래스로 구현하며, 설명을 리턴하는 desc와 가격을 리턴하는 cost 메서드를 갖는다.
실제 인스턴스(커피 차 에이드등)는 음료 클래스를 상속하여 사용한다.
문제 정의
고객이 추가한 옵션에 따라 음료의 가격을 리턴해줘야 한다..
고객이 추가할 수 있는 옵견은 아래 5가지로 가정한다.
가장 쉬운 접근 방식은 가능한 모든 옵션 조합을 클래스로 만들어 주는 것이다.
예를 들면 (우유o 카페인o 시럽x 모카 x 초콜릿 o) > MilkCaffeineChocolete
문제점
이러면 2^5, 32개의 클래스를 만들어 줘야한다.
옵션 삭제: 해당 조건을 갖고 있는 클래스를 일일이 찾아서 삭제해 줘야한다.
옵션 추가: 32개의 클래스를 다시 만들어 줘야 한다.
접근: 메뉴는 알맹이를 표현하고, 조건을 음료 class에서 처리한다
해당 조건을 boolen으로 클래스에 입력할 수 있도록 한다.
cost함수에서 hasmoca, hascaffeine과 같은 메서드로 조건을 검사하여 cost를 추가한뒤 return한다.
문제점
1) cost 함수 자체가 지저분 하다.
2) 모카나 카페인 밀크와 아이에 상관이 없는 메뉴 또한 해당 옵션에 대한 메서드를 갖는다.
음료는 알맹이다. 그리고 조건에 대한 부분을 Decorator로서 껍떼기 처럼 감싸 사용할 수 있다.
알맹이(component)에 껍떼기(Decorator)를 감싸서, 알맹이를 기능이 추가된 상태로 사용 할 수 있다.
알맹이와 껍떼기 구조는 decorator class가 component(알맹이) 클래스를 상속(is_a)함과 동시에 보유(has_a)하여 구현된다.
is_a:껍떼기 또한 알맹이 처럼 행동해야 하기 때문에, Component를 상속(is_a)한다.
has_a:껍떼기가 알맹이에 접근해야 하기 때문에, Component를 갖는다(has_a).
class Beverage:
def desc(self)->str:
return "Beverage"
def cost(self)->int:
return 0
class Coffee(Beverage):
def desc(self)->str:
return "coffee"
def cost(self)->int:
return 4000
class AddonBeverageDecorator(Beverage):
def __init__(self,beverage):
self.beverage = beverage
def desc(self)->str:
return self.beverage.desc()
def cost(self)->int:
return self.beverage.cost()
class MilkDecorator(AddonBeverageDecorator):
def desc(self)->str:
return '{} + Milk'.format(self.beverage.desc())
def cost(self) ->int:
return self.beverage.cost()+500
caffelatte = MilkDecorator(Coffee())
print(caffelatte.desc())
print(caffelatte.cost())
python은 Ducktyping 방식으로 동작된다. 또한 다중 상속을 지원한다.
python은 interface와 abstarct 클래스를 따로 지원하지 않는다.
python에서 decorator를 함수를 wrapping 하는 방식으로 주로 사용하는 것 처럼 보인다.
def f1(f):
def wrapper(*args,**kwargs):
print("before f")
ret = f(*args,**kwargs)
print("after f")
return ret
return wrapper
@f1
def f(a):
print(a)
return a
print(f("Hi!"))
Blog:
Refactoring guru - decorator
Youtube:
Christopher Okhravi - Decorator Patter
Python Decorators in 15 Minutes