먼저 기본 기능을 선언해놓은 Component와 이를 구현한 ConcreteComponent입니다.
operation 메서드를 호출하면 "Basic Operation" 문자를 출력합니다.
protocol Decorator: Component {
var component: Component { get set }
}
class ConcreteDecoratorA: Decorator {
var component: Component
init(component: Component) {
self.component = component
}
func operation() {
component.operation()
print("A Operation")
}
}
class ConcreteDecoratorB: Decorator {
var component: Component
init(component: Component) {
self.component = component
}
func operation() {
component.operation()
print("B Operation")
}
}
class ConcreteDecoratorC: Decorator {
var component: Component
init(component: Component) {
self.component = component
}
func operation() {
component.operation()
print("C Operation")
}
}
Decorator의 기능을 선언한 Decorator Protocol과 이를 구현한 ConcreteDecoratorA,B,C 입니다.
Decorator는 내부에 Component를 들고 있고 이를 활용할 수 있습니다.
let basic = ConcreteComponent()
let a = ConcreteDecoratorA(component: basic)
let b = ConcreteDecoratorB(component: basic)
let c = ConcreteDecoratorC(component: basic)
a.operation()
/* Basic Operation
A Operation */
b.operation()
/* Basic Operation
B Operation */
c.operation()
/* Basic Operation
C Operation */
이제 이들을 Client에서 활용해봅시다.
기본적인 기능을 가진 ConcreteComponent 인스턴스를 생성하고, 이를 활용해 ConcreteDecoratorA,B,C 인스턴스를 생성해봅시다.
그리고 operation을 출력하면 Basic Operation과 각 Operation이 함께 출력됩니다.
여기까지는 매우 당연하게 느껴지죠? Decorator의 진짜 매력은 이 다음에 나옵니다.
let ab = ConcreteDecoratorB(component: a)
let abc = ConcreteDecoratorC(component: ab)
ab.operation()
/* Basic Operation
A Operation
B Operation */
abc.operation()
/* Basic Operation
A Operation
B Operation
C Operation */
Decorator는 반복적으로 기능을 더할 수 있습니다.
기본 ConcreteComponent에 A를 더하고 B를 더할 수 있습니다.
여기서 멈추지 않고 C를 더하는 것도 가능합니다.
이렇게하면 다양한 조합의 타입(ConcreteDecoratorAB, ConcreteDecoratorAC, ConcreteDecoratorBC, ...)을 각각 생성하지 않고 조합을 통해 만들 수 있을 것입니다.
정리
Decorator 패턴을 사용하면 객체의 기능을 동적으로 추가할 수 있겠네요. 객체의 단일책임원칙도 지킬 수 있구요.
하지만 이를 겹겹히 애워싸면서 객체를 구성하다보면 결국 객체의 정체를 제대로 파악할 수 없고, 스택과 같은 형태로 기능이 쌓이기 때문에 중간에 존재하는 특정 데코레이터를 삭제하는 것이 어렵습니다.