Motivation: We want to add responsibilities to an object dynamically at runtime, rather than statically through inheritance. Subclassing every combination of features would lead to a combinatorial explosion of classes.
Intent: Attach additional responsibilities to an object dynamically, providing a flexible alternative to subclassing.
Decorators are seen a lot in Python and Java and these wrap components and forward calls to a method, adding behaviour before or after. These can be stacked. Each decorator stays focused on a single responsibility.
interface Beverage {
fun cost(): Double
fun description(): String
}
class Coffee : Beverage {
override fun cost() = 1.5
override fun description() = "Coffee"
}
class Tea : Beverage {
override fun cost() = 1.0
override fun description() = "Tea"
}
abstract class BeverageDecorator(private val beverage: Beverage) : Beverage {
abstract override fun cost(): Double
abstract override fun description(): String
}
class MilkDecorator(beverage: Beverage) : BeverageDecorator(beverage) {
override fun cost() = super.cost() + 0.5
override fun description() = super.description() + " + milk"
}
class SugarDecorator(beverage: Beverage) : BeverageDecorator(beverage) {
override fun cost() = super.cost() + 0.2
override fun description() = super.description() + " + sugar"
}
// Decorators can be composed freely
val order: Beverage = MilkDecorator(SugarDecorator(Coffee()))
// -> Coffee + sugar + milk