의도
객체에 동적으로 새로운 책임을 추가할 수 있게 합니다.
기능을 추가하려면, 서브클래스를 생성하는 것보다 융통성 있는 방법을 제공합니다.
다른이름
Wrapper
동기
가끔 전체 클래스에 새로운 기능을 추가할 필요는 없지만, 개별적인 객체에 새로운 책임을 추가할 필요가 있다.
예를 들어, GUI 툴킷에서 모든 사용자 인터페이스 요소에는 필요가 없지만, 몇몇 사용자 인터페이스 요소에만 스크롤링이나 테두리 등과 같은 속성을 추가할 수 있도록 해줄 필요는 있다. 하나의 객체에 속성이 추가됨으로써 또 다른 책임이 추가되어야 함.
(책임이란게 어떤 메소드를 구현해줘야할 책임?)
이렇게 새로운 서비스의 추가가 필요할 때 기본적인 방법은 상속을 이용하는 것.
즉, 이미 존재하는 클래스를 상속받고 상속받은 클래스에서 테두리가 있도록 하는 방법인데 별로 유용하지 않다.
테두리의 선택이 정적이기 때문.
사용자는 구성요소를 언제, 그리고 어떻게 테두리로 장식해야할 지 제어할 수 없다.
더 나은 방법은 필요한 테두리를 추가하는 다른 객체에 해당 구성요소를 둘러싸는 것이다. 이렇게 무엇인가를 감싸는 객체를 Decorator라고 한다.
Decorator는 자신이 둘러싼 요소, 구성요소(Component)가 갖는 인터페이스를 자신도 동일하게 갖는다. 따라서 Decorator의 존재는 사용자에게 감춰진다.
장식자는 구성요소로 전달되는 요청을 중간에 가로채서 해당 구성요소에 전달해주는 역할을 한다.
그렇기 때문에 이 전달 과정의 앞뒤에 다른 작업(테두리 그리기 등)을 추가로 할 수 있다.
예를들어, TextView가 있을 때, Scrollbar 기능을 필요로하게 된다면 ScrollDecorator를 통해 TextView에 스크롤바를 추가할 수 있다.
또 두꺼운 테두리가 필요하면 BorderDecorator를 만들어서 추가적인 서비스 문제를 해결 할 수 있다.
이런식으로 구성할 수도 있다.
위의 그림을 다이어그램으로 나타낸 것.
ScrollDecorator와 BorderDecorator는 Decorator의 서브클래스이며, 다른 VisualComponent를 감싸는 VisualComponent들을 위한 추상클래스이다.
VisualComponent는 비주얼 객체들을 위한 추상클래스로, Draw와 기타 이벤트 처리에 대한 인터페이스를 정의한다.
Decorator클래스는 단순하게 Draw()콜이 오면 자신이 갖고 있는 component에 전달 ( component->Draw() ) 할 뿐.
Decorator의 서브클래스는 Draw() 메소드를 확장하여 필요한 기능을 구현한다. 위에서 말했던 전달과정 앞뒤에 다른작업을 추가로 함.
Decorator는 필요한 서비스를 구현하기 위해 추가 구현할 수 있다. 예를들어, ScrollDecorator는 스크롤 기능을 위해 ScrollTo()라는 메소드를 추가한다던지. BorderDecorator의 DrawBorder().
활용성
- 동적으로 또한 투명하게, 다시말해 다른객체에 영향을 주지 않고 개개의 객체에 새로운 책임을 추가하기 위해 사용.
- 제거될 수 있는 책임에 대해 사용.
- 실제 상속을 서브클래스를 계속 만드는 방식이 실질적이지 못할때 사용. 너무 많은 수의 독립된 확장이 가능할때 모든 조합을 지원하기 위해 이를 사용. 아니면 클래스 정의가 숨겨지던가, 그렇지 않더라도 서브클래싱으 할 수 없게 됩니다. (?)
구조
Component : 동적으로 추가할 서비스를 가질 가능성이 있는 객체들에 대한 인터페이스.
ConcreteComponent : 추가적인 서비스가 실제로 정의되어야 할 필요가 있는 객체.
Decorator : Component 객체에 대한 참조자를 관리하면서 Component에 정의된 인터페이스를 만족하도록 인터페이스를 정의.
ConcreteDecorator : Component에 새롭게 추가할 서비스를 실제로 구현하는 클래스.
예제
class VisualComponent {
public:
VisualComponent();
virtual void Draw();
virtual void Resize();
//...
};
class Decorator : public VisualComponent {
public:
Decorator(VisualComponent* component) {
_component = component;
}
virtual void Draw() {
_component->Draw();
}
virtual void Resize() {
_component->Resize();
}
//..
private:
VisualComponent* _component;
};
class BorderDecorator : public Decorator {
public:
BorderDecorator(VisualComponent* component, int borderWidth)
: Decorator(component) {
_width = borderWidth;
}
virtual void Draw() {
Decorator::Draw();
DrawBorder();
}
//..
private:
void DrawBorder() {
//...
}
int _width;
};
class ScrollDecorater : public Decorator {
//..
};
///////////////////////////////////////////////////////////////////////////////
class Window {
public:
void Window::SetContents(VisualComponent* contents) { }
//...
};
class TextView : public VisualComponent {
//....
};
void main() {
//...
Window* window = new Window;
TextView * textView = new TextView;
window->SetContents(
new BorderDecorator(new ScrollDecorater(textView),1));
//...
}
결과
- 단순한 상속보다 설계의 융통성을 더 많이 증대시킨다. : 상속은 코드에서 정적으로 새로운 클래스를 만들어야 새로운 조합을 할 수 있는 반면, 데코레이터 패턴은 런타임에 새로운 조합을 만들어 낼 수 있다.
- 클래스 계통의 상부측 클래스에 많은 기능이 누적되는 상황을 피할 수 있다. : "필요한 비용한 그때 지불하는 방식"을 제공. 개발시 현재 사용되지 않는 기능까지 개발하기 위해 노력하지 않아도, Decorator패턴을 통해 추가할 수 있다.
- Decorator와 해당 Component가 동일한 것은 아니다. : 데코레이터는 사용자에게 일관된 인터페이스를 제공하는 껍데기. 그러므로 객체 식별자 관점(사용하는 입장?)
에서 컴포넌트와 이를 둘러싼 데코레이터 객체가 동일한 식별자를 가질 필요는 없다.
- (Decorator가 꼭 Component를 상속받아야하는 건아니다? )
- 장식자를 사용함으로써 작은 규모의 객체들이 많이 생긴다. : 클래스들을 어떻게 조합하여 새로운 모습을 나타내는가에 따라 새로운 객체가 계속해서 만들어지기 때문에 작은 규모의 객체들이 많이 생긴다. 이때 이 객체들을 잘 이해하고 있다면 시스템의 재정의가 쉽겠지만, 그렇지 않다면 객체들을 모두 이해하고 수정하는 과정이 복잡해진다. ?
구현
- 인터페이스 일치시키기
- 추상클래스로 정의되는 Decorator 클래스 생략하기. : 가끔 추상클래스인 Decorator가 필요 없을때 도 있음. 단순히 전달하는 역할. 그 역할을 ConcreteDecorator와 합칠 수 있다.
- Component 클래스는 가벼운 무게를 유지하기 : 인터페이스만 정의하고 무언가 변수를 넣지 말라는 의미. 변수를 넣으면 상속받는 여러 Decorator들도 무거워짐. 또한 Component에 너무 많은 서비스를 정의하는 것도 서브클래스에는 부담이 된다. 서브클래스 입장에서는 필요 없는 연산까지도 적절한 구현을 해야하기 때문.
- 객체의 겉포장을 변경할 것인가, 속을 변경할 것인가. : 속까지 변경시킨다면 전략(Strategy) 패턴을 쓰는게 좋음. 뒤에 나옴.
잘 알려진 예제
관련 패턴
적응자 (Adapter) 패턴과 관련되어 있다. 장식자는 어쩌면 적응자의 일종. 즉 원래의 적응자는 인터페이스를 변경시켜주는 것이었지만, 장식자는 객체의 책임, 행동을 변화시킨다.
복합체 패턴과도 관련 있음. 장식자는 하나의 Component만을 갖는 복합체로 볼수 있다. 그러나 이 목적은 객체의 합성이 아니라 객체에 새로운 행동을 추가하기 위한 것이다.
전략패턴과도 관련 있음. 장식자는 객체의 겉모양을 변경하고, 전략은 객체의 내부를 변화시킨다. 객체를 변경하는 두가지 대안인 셈.
'스터디 > 디자인패턴' 카테고리의 다른 글
중재자 (MEDIATOR) 패턴 (0) | 2017.01.05 |
---|