본문 바로가기

Design/Design Pattern

[Design Pattern] Decorator Pattern

이번 장에서는 데코레이터(Decorator) 패턴에 대해서 알아본다.
샘플 코드는 여기 (링크) 프로젝트의 테스트 코드로 정리해두었다.


데코레이터 패턴이란?
위키백과에 따른 정의는 아래와 같다.

주어진 상황 및 용도에 따라 어떤 객체에 책임을 덧붙이는 패턴으로, 기능 확장이 필요할 때 서브클래싱 대신 쓸 수 있는 유연한 대안이 되는 디자인 패턴.

쉽게 말해서 사용중인 객체에 추가 기능이 필요한 경우 해당 객체를 확장(상속)하는 것이 아니라 데코레이터를 통해 기능을 추가하는 패턴이다.
필자는 아래의 이미지에서 햄, 참치, 베지 샌드위치를 만드는 과정을 예시로 살펴볼 것이다.


Gof Design Pattern에 따르면 아래와 같은 Class Diagram이 그려진다.

Component: 실제 구현체와 Decorator들의 최상위 추상 클래스
필자는 Decorator의 최상위에 Component가 존재한다는 것을 이해하는데 큰 시간을 소비하였다. 하단부에서 코드를 왜 저런 구성도가 나오는지 확실하게 이해할 수 있을 것이다.

ConcreteComponent: 구현체 클래스

Decorator: 구현체를 Decorate할 클래스들의 추상 클래스

ConcreteDecoratorA, ConcreteDecoratorB: 구현체를 Decorate할 클래스

위의 이미지를 서브웨이 샌드위치에 적용시키면 아래와 같은 그림이 나온다.

Component 역할을 하는 Sandwich가 있고 이를 구현한 DefaultSandwich가 있다.
Topping 추상 클래스 또한 Sandwich를 구현하고 있으며 Topping을 확장한 Ham, Cheese, Tuna, Vegetable이 있다.


코드를 보면서 하나씩 살펴보도록 한다.

Sandwich Class
이름과 토핑들 속성을 가지고 있으며 cost() 메서드는 자신을 확장한 클래스들에게 위임(Delegation)하고 있다.
getToppingNames 메서드는 단순히 토핑들의 이름을 출력하기 위한 코드이며 이번 패턴과는 무관하므로 이해가 되지 않는다면 무시해도 좋다.

DefaultSandwich Class
실제 구현체가 되는 클래스.
생성될 때 이름이 지정되고 기본 가격인 3000원을 가지고 있다.

Topping Class
Decorator인 Topping들의 추상 클래스다. Sandwich를 확장하고 있다.

Cheese, Ham, Tuna, Vegetable Class
Decorator 클래스들로서 DefaultSandwich를 Decorate하는 역할을 한다.
필자는 이 부분을 이해하는데 시간이 오래 걸렸다.
Cheese 코드를 보면 Sandwich를 파라미터로 받아서 Cheese를 Return하고 있다.
왜 구현체를 받아서 데코레이터를 Return하는거지?! 라는 의문이 들었다.
Class Diagram을 다시 살펴보면 Cheese 또한 Sandwich를 확장하고 있는 것을 알 수 있다.
그렇다. 최초에는 DefaultSandwich였지면 Cheese Decorator를 통하게 되면 DefaultSandwich가 아닌 Cheese가 되는 것이다.
기존 객체를 수정하는 것이 아니라 기존 객체로 Decorate된 새로운 객체를 만들어내는 것이 Decorator 패턴인 것이다.

Customer Class
샌드위치를 생성하는 클래스

Customer 코드를 실행시킨 결과는 아래와 같다.


지금까지 서브웨이 샌드위치를 주제로 데코레이터 패턴에 대해서 알아보았다.
필자가 디자인 패턴을 공부하면서 이해하는데 가장 오랜 시간이 걸린 패턴인데 이 글을 읽는 사람들은 그렇지 않길 바란다.