Python/Design Patterns

데코레이터(Decorator)

코닝구 2020. 9. 22. 00:40

디자인 패턴 중에서 데코레이터(Decorator)에 대해서 알아보자. 데코레이터 패턴은 GoF가 제시한 구조 패턴(Structural Pattern) 중의 하나로 어떤 한 클래스의 인스턴스 동작을 변경하지 않고, 새로운 인스턴스에 동작을 다이나믹하게 추가하는 패턴이다.

https://refactoring.guru

데코레이터 패턴은 기능이 확장될 때 서브클래싱(subclassing)의 대안으로 사용할 수 있다. 그리고 데코레이터 패턴은 객체지향 5대 원칙(SOLID) 중 OCP(Open-Close Principle)에 해당한다. OCP는 확장에는 개방되고, 변경에는 폐쇄한다는 원칙인데, 이는 변경을 위한 비용은 가능한 줄이고, 확장을 위한 비용은 가능한 극대화해야 한다는 의미이다. 즉, 기존의 소프트웨어를 쉽게 확장해서 재사용할 수 있어야 한다는 것이다.


기본 개념

데코레이터 패턴에는 ConcreateComponent 클래스와 Decorator 클래스로 구현되는 Component 라는 인터페이스가 있다.

Decorator UML class diagram

Component: 추상 클래스(abstract class)로 ConcreateComponent 클래스와 Decorator 클래스의 슈퍼 클래스이다. 이 두 클래스에서 사용할 표준 메소드를 정의한다.

 

ConcreateComponent: Component 클래스의 서브클래스로 표준 메소드를 구체화한다.

 

Decorator: 추상 클래스(abstract class)로 Component 클래스의 서브클래스이자 ConcreateDecorator 클래스의 슈퍼 클래스이다.

 

ConcreateDecorator: Decorator 클래스의 서브클래스로 기능을 추가하여 Component를 장식하는 클래스이다. 추가되는 기능에 따라 ConcreateDecoratorA(), ConcreateDecoratorB() 등 계속 확장할 수 있다.


구현

데코레이터 패턴의 대부분의 예제는 커피를 이용하고 있다. 커피에는 아메리카노, 카페모카 등 커피의 기본가격이 있고, 샷추가, 휘핑 추가에 따라 가격이 달라짐으로 데코레이터 패턴을 구현하기 매우 적합하다.

from abc import ABC, abstractmethod


class Coffee(ABC):
    """Interface"""

    @abstractmethod
    def cost(self):
        pass


class Americano(Coffee):
    """ConcreateComponent"""

    def cost(self):
        return 2000


class Moka(Coffee):
    """ConcreateComponent"""

    def cost(self):
        return 4000


class CoffeeDecorator(Coffee, ABC):
    """Decorator"""

    def __init__(self, coffee):
        self._coffee = coffee

    def cost(self):
        return self._coffee.cost()


class Shot(CoffeeDecorator):
    """ConcreateDecorator"""

    def cost(self):
        return self._coffee.cost() + 500


class Whip(CoffeeDecorator):
    """ConcreateDecorator"""

    def cost(self):
        return self._coffee.cost() + 1000


if __name__ == '__main__':
    o1 = Americano()
    o2 = Moka()
    o3 = Whip(Shot(Moka()))

    print(o1.cost())  # 2000
    print(o2.cost())  # 4000
    print(o3.cost())  # 5500

코드에서 o3 객체를 보면 Moka를 Whip(), Shot()으로 래핑(wrapping)하여 장식하고 있는것을 알 수 있다.


장점

  1. 런타임시에 특정 객체에 대해서만 추가 기능을 확장할 수 있다.
  2. 서브클래스는 컴파일시에 기능이 추가되어 모든 인스턴스에 영향을 주지만 데코레이터는 특정 객체에 대해서만 기능을 확장한다.
  3. 추가되는 기능에 대해 유연하게 대처할 수 있다.

단점

  1. 래핑의 수가 많을수록 복잡도가 증가한다.
  2. 기능이 추가될수록 가독성이 떨어진다.

'Python > Design Patterns' 카테고리의 다른 글

템플릿 메소드(Template Method)  (0) 2020.09.30
싱글턴(Singleton)  (0) 2020.09.29
퍼사드(Facade)  (0) 2020.09.27
보그(Borg)  (0) 2020.09.27