객체에 추가적인 요건을 동적으로 첨가한다.
데코레이터는 서브클래스를 만드는 것을 통해서 기능을 유연하게 확장할 수 있는 방법을 제공한다.
상속은 is-a
구성은 has-a
상속을 대체할 수는 없습니다.
다만 상속을 통해 하고자하는 일이 다릅니다!
데코레이터에서의 상속은
오버라이딩을 통하여 메소드의 행동을 변경하고 올바른(필요한) 형식을 맞추기 위해서 하는 것입니다!
객체의 구성(인스턴스 변수로 다른 객체를 저장하는 방식)을 통하여 유연성을 보장합니다!
package decorator
abstract class Beverage(
var description: String = "제목 없음"
) {
open fun getDes() = description
abstract fun cost(): Double
}
추상클래스 or 인터페이스 모두 가능.
package decorator
class Espresso: Beverage() {
init {
description = "에스프레소"
}
override fun cost(): Double {
return 1.99
}
}
모든 첨가물 데코레이터에서 getDes()를 통하여 표현할 수 있습니다!
package decorator
abstract class CondimentDecorator: Beverage() {
abstract override fun getDes(): String
}
package decorator
class Mocha(
private var beverage: Beverage
): CondimentDecorator() {
override fun cost(): Double {
return beverage.cost() + .20
}
override fun getDes(): String {
return beverage.getDes() + ", 모카" // 재귀는 아님
}
}
package decorator
class Whip(
private var beverage: Beverage
) : CondimentDecorator() {
override fun cost(): Double {
return beverage.cost() + .59
}
override fun getDes(): String {
return beverage.getDes() + ", 휘핑 크림"
}
}
package decorator
fun main() {
val beverage = Espresso()
println(beverage.description)
var beverage2: Beverage = HouseBlend()
beverage2 = Mocha(beverage2)
beverage2 = Mocha(beverage2)
beverage2 = Whip(beverage2)
println(beverage2.getDes())
}
// 결과
// 에스프레소
// 하우스 블렌드 커피, 모카, 모카, 휘핑 크림
// 하우스 블렌드 커피, 모카, 휘핑 크림, 모카
구상 구성요소(모카, 휘핑...)의 형식을 알아내서 결과를 바탕으로 어떤 작업을 처리하는 코드에는 적합하지 않습니다!
특정 형식에 의존하는 코드에 데코레이터를 그냥 적용해버리면 코드가 엉망이 됩니다!
그러다보니, 구성요소를 초기화하는 데 필요한 코드가 훨씬 복잡해집니다.
→ 특정 데코레이터 내부에서 인스턴스 변수(Component component)가 참조하고 있는 구상 컴포넌트의 구현체를 알 수 없습니다.(Whip 클래스의 구성 클래스가 어떤 데코레이터로 감싸져 있는지 모른다는 뜻) 따라서, 데코레이터 수행 과정 중 구상 컴포넌트에 특정 작업을 할 수 없습니다!
구성요소를 초기화하는데 필요한 코드가 훨씬 복잡해집니다.
→ 이러한 문제는 팩토리 패턴이나 빌더 패턴을 사용함으로써 해결이 가능합니다!
데코레이터 패턴의 Component가 Product로 들어가면서, 팩토리 메서드 패턴을 사용하였습니다!
코드로 작성해보면,
package decorator
abstract class BeverageStore {
abstract fun createBeverage(type: String): Beverage
}
package decorator
class StarbuzzStore: BeverageStore() {
override fun createBeverage(type: String): Beverage {
var targetBeverage: Beverage = Espresso()
if(type == "HouseBlend") targetBeverage = HouseBlend()
return targetBeverage
}
}
package decorator
fun main() {
val starbuzzStore = StarbuzzStore()
val beverage = starbuzzStore.createBeverage("Espresso")
println(beverage.description)
var beverage2 = starbuzzStore.createBeverage("HouseBlend")
beverage2 = Mocha(beverage2)
beverage2 = Mocha(beverage2)
beverage2 = Whip(beverage2)
println(beverage2.getDes())
}
// 결과
// 에스프레소
// 하우스 블렌드 커피, 모카, 모카, 휘핑 크림
이런식으로 사용이 가능할 것 같습니다!