장식자 패턴(Wrapper)
- 객체에 동적으로 새로운 책임을 추가할 수 있는 패턴, 기능 추가에서 서브클래싱 보다 융통성 있는 방법을 제공.
- 객체들을 새로운 행동들을 포함한 특수 래퍼 객체들 내에 넣어서 위 행동들을 해당 객체들에 연결.
활용성
- 다른 객체에 영향을 주지 않고 개개의 객체에 새로운 책임을 추가할 때 사용
- 제거될 수 있는 책임에 대해 사용
- 상속으로 서브클래스를 계속 만드는 방법이 적절하지 않을 때 사용.
구조
요소
- 컴포넌트(Component)
- 동적으로 추가할 서비스를 가질 가능성이 있는 객체들의 인터페이스
- 구상 컴포넌트(ConcreteComponent)
- 추가적인 서비스가 실제로 정의되어야 할 필요가 있는 객체
- 데코레이터(Decorator)
- 객체에 대한 참조자를 관리하면서 컴포넌트에 정의된 인터페이스를 만족하도록 인터페이스를 정의
- 구상 데코레이터(ConcreteDecorator)
- 컴포넌트에 새롭게 추가할 서비스를 실제로 구현
협력 방법
- 데코레이터는 자신의 컴포넌트 객체쪽으로 요청을 전달.
장점
- 단순한 상속보다 설계의 융통성 증대.
- 런타임에 데코레이터를 객체와 연결하거나 분리하는 작업을 통해 새로운 책임을 추가하거나 삭제하는 일이 가능.
- 상위 클래스에 많은 기능이 누적되는 상황을 피할 수 있다.
- 필요한 기능만 개발하고 누락된 서비스들을 데코레이터 객체를 통해 지속적으로 추가 가능.
- 단일 책임 원칙.
- 다양한 행동들의 여러 변형들을 구현하는 모놀리식 클래스를 여러 개의 작은 클래스들로 나눌 수 있다.
단점
- 래퍼들의 스택에서 특정 래퍼를 제거하기가 어렵다.
- 데코레이터의 행동이 데코레이터 스택 내의 순서에 의존하지 않는 방식으로 데코레이터를 구현하기가 어렵다.
- 계층들의 초기 설정 코드가 보기 흉할 수 있다.
예시 코드
참고
protocol Coffee {
func cost() -> Double
func description() -> String
}
class SimpleCoffee: Coffee {
func cost() -> Double {
return 2.0
}
func description() -> String {
return "Simple Coffee"
}
}
class MilkDecorator: Coffee {
let coffee: Coffee
init(coffee: Coffee) {
self.coffee = coffee
}
func cost() -> Double {
return coffee.cost() + 1.0
}
func description() -> String {
return coffee.description() + ", Milk"
}
}
class SugarDecorator: Coffee {
let coffee: Coffee
init(coffee: Coffee) {
self.coffee = coffee
}
func cost() -> Double {
return coffee.cost() + 0.5
}
func description() -> String {
return coffee.description() + ", Sugar"
}
}
var coffee: Coffee = SimpleCoffee()
print("Cost: \(coffee.cost()), Description: \(coffee.description())")
coffee = MilkDecorator(coffee: coffee)
print("Cost: \(coffee.cost()), Description: \(coffee.description())")
coffee = SugarDecorator(coffee: coffee)
print("Cost: \(coffee.cost()), Description: \(coffee.description())")