Decorator Pattern

김현준·2021년 3월 7일
0

design-pattern

목록 보기
1/1

TIL: Decorator Pattern (class 상속)

Decorator Pattern을 알아보았다. Decorator 정의를 살펴보자.

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가지로 가정한다.

  • 우유 추가
  • 카페인 추가
  • 시럽 추가
  • 모카 추가
  • 초콜릿 추가

가장 쉬운 접근과 그 접근 방식의 문제점 > classexplosion

가장 쉬운 접근 방식은 가능한 모든 옵션 조합을 클래스로 만들어 주는 것이다.
예를 들면 (우유o 카페인o 시럽x 모카 x 초콜릿 o) > MilkCaffeineChocolete

문제점
이러면 2^5, 32개의 클래스를 만들어 줘야한다.

옵션 삭제: 해당 조건을 갖고 있는 클래스를 일일이 찾아서 삭제해 줘야한다.
옵션 추가: 32개의 클래스를 다시 만들어 줘야 한다.

boolean method를 사용한 방식과 문제점 > Tea가 추가 될 경우

접근: 메뉴는 알맹이를 표현하고, 조건을 음료 class에서 처리한다

해당 조건을 boolen으로 클래스에 입력할 수 있도록 한다.
cost함수에서 hasmoca, hascaffeine과 같은 메서드로 조건을 검사하여 cost를 추가한뒤 return한다.

문제점
1) cost 함수 자체가 지저분 하다.
2) 모카나 카페인 밀크와 아이에 상관이 없는 메뉴 또한 해당 옵션에 대한 메서드를 갖는다.

Decorator

음료라는 알맹이와, 추가 기능을 설명하는 껍떼기

음료는 알맹이다. 그리고 조건에 대한 부분을 Decorator로서 껍떼기 처럼 감싸 사용할 수 있다.
알맹이(component)에 껍떼기(Decorator)를 감싸서, 알맹이를 기능이 추가된 상태로 사용 할 수 있다.

알맹이와 껍떼기는 가능하게 만들어 주는 상속 구조 is_a, has_a

알맹이와 껍떼기 구조는 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에서 Decorator

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

profile
chandlerkim

0개의 댓글