decorator-pattern
TL;DR⚑
- 데코레이터 패턴은 객체에 동적으로 새로운 기능을 추가할 수 있게 해주는 디자인 패턴임.
- 이 패턴은 상속을 사용해 기능을 확장하는 것보다 더 유연한 방법을 제공함.
- 런티임에 객체에 새로운 기능을 추가할 수 있음.
- 기능 조합에 유연성
- 기능의 독립적인 추가 및 제거
- 클래스의 폭발 문제 해결
Code Example
from abc import ABC, abstractmethod
# Component 인터페이스
class VisualComponent(ABC):
@abstractmethod
def draw(self):
pass
# ConcreteComponent
class TextView(VisualComponent):
def draw(self):
print("Drawing a text view")
# Decorator - 데코레이터는 감싸는 대상의 인터페이스를 구현해야 하며,
# 추가기능을 구현 후 self._component 를 통해서 나머지 처리를 위임한다.
class Decorator(VisualComponent):
def __init__(self, component: VisualComponent):
self._component = component
def draw(self):
self._component.draw()
# ConcreteDecoratorA
class BorderDecorator(Decorator):
def draw(self):
super().draw()
self._draw_border()
def _draw_border(self):
print("Adding a border")
# ConcreteDecoratorB
class ScrollDecorator(Decorator):
def draw(self):
super().draw()
self._draw_scroll()
def _draw_scroll(self):
print("Adding scroll bars")
# 클라이언트 코드
if __name__ == "__main__":
# 기본 TextView 객체 생성
text_view = TextView()
# 텍스트 뷰에 테두리를 추가
bordered_text_view = BorderDecorator(text_view)
# 테두리가 추가된 텍스트 뷰에 스크롤을 추가
scrollable_bordered_text_view = ScrollDecorator(bordered_text_view)
# 최종 객체의 draw 메서드를 호출
scrollable_bordered_text_view.draw()
설명⚑
VisualComponent
는 기본 인터페이스 또는 추상 클래스임.TextView
는 기본 구현체로, 단순히 텍스트를 출력하는 역할을 함.Decorator
는VisualComponent
를 상속받아draw
메서드를 호출하도록 하고, 데코레이터들이 이 클래스를 상속받아 추가 기능을 구현할 수 있도록 함.BorderDecorator
와ScrollDecorator
는 각각 테두리와 스크롤 기능을 추가하는 데코레이터임.- 최종적으로,
scrollable_bordered_text_view
객체를 사용하여draw
메서드를 호출하면 기본 텍스트 뷰에 테두리와 스크롤이 추가된 출력이 나옴.
데코레이터 패턴이란?⚑
- 데코레이터 패턴은 객체에 새로운 책임을 동적으로 추가할 수 있게 해주는 패턴임.
- 서브클래스를 생성하지 않고도 객체에 기능을 추가할 수 있어 더 융통성이 있음.
- Wrapper 패턴이라고도 불림.
사용 동기⚑
- 전체 클래스에 새로운 기능을 추가할 필요는 없지만, 특정 객체에만 기능을 추가하고 싶을 때 유용함.
- 상속을 통해 기능을 추가할 수도 있지만, 이 방법은 유연성이 부족할 수 있음.
- 예를 들어, GUI의 스크롤 기능이나 테두리 추가 기능이 필요할 때 데코레이터 패턴을 사용할 수 있음.
구조와 참여자⚑
- Component: 동적으로 추가할 수 있는 서비스를 가질 가능성이 있는 객체에 대한 인터페이스임.
- ConcreteComponent: 실제로 추가적인 서비스가 정의될 필요가 있는 객체임.
- Decorator: Component에 대한 참조자를 관리하며, Component의 인터페이스를 구현함.
- ConcreteDecorator: Component에 새롭게 추가할 서비스를 실제로 구현하는 클래스임.
데코레이터 패턴의 장단점⚑
- 장점
- 단순 상속보다 설계의 융통성을 증가시킬 수 있음.
- 클래스 계층 구조의 상위 클래스에 불필요한 기능이 누적되는 것을 방지할 수 있음.
- 단점
- 장식자와 그 장식자가 꾸미는 객체가 동일하지 않을 수 있음. 이는 식별자 이슈를 야기할 수 있음.
- 작은 규모의 객체들이 많이 생기므로 관리 비용이 증가할 수 있음.
데코레이터 패턴의 활용성⚑
- 동적으로 또는 투명하게, 다른 객체에 영향을 주지 않고 개별 객체에 기능을 추가할 수 있음.
- 필요에 따라 제거할 수 있는 책임을 추가할 때 사용함.
- 상속을 사용한 방법이 실질적이지 못할 때 유용함. 특히, 많은 독립적인 확장이 가능할 때 이를 모두 상속으로 해결하면 클래스의 수가 폭발적으로 증가할 수 있음.
데코레이터 패턴 구현 시 고려사항⚑
- 데코레이터는 반드시 구성 요소의 인터페이스를 만족시켜야 함.
- ConcreteDecorator는 동일한 부모 클래스를 상속받아야 함.
- 데코레이터 클래스를 추상 클래스로 정의할 필요가 없는 경우 이를 생략할 수 있음.
- Component 클래스는 가능한 가볍게 유지하는 것이 좋음.
관련 패턴⚑
- 데코레이터 패턴은 적응자 (Adapter) 패턴과 유사함. 적응자가 인터페이스를 변경하는 것이라면, 데코레이터는 객체의 책임과 행동을 변경함.
- 복합체 (Composite) 패턴과도 관련이 있음. 다만, 복합체는 객체의 합성을 목표로 하지만, 데코레이터는 객체에 새로운 행동을 추가하는 데 목적이 있음.
- 전략 (Strategy) 패턴과는 객체를 변화시키는 두 가지 대안임. 데코레이터는 객체의 겉모습을 변화시키고, 전략 패턴은 객체의 내부 로직을 변화시킴.