The before code uses subclassing to add milk and sugar costs, leading to many subclasses for combinations. The after code wraps the base Coffee object with decorators that add cost dynamically, allowing flexible feature combinations without subclass explosion.
### Before (without decorator pattern):
class Coffee:
def cost(self):
return 5
class MilkCoffee(Coffee):
def cost(self):
return super().cost() + 2
class SugarMilkCoffee(MilkCoffee):
def cost(self):
return super().cost() + 1
coffee = SugarMilkCoffee()
print(coffee.cost()) # Output: 8
### After (with decorator pattern):
class Coffee:
def cost(self):
return 5
class CoffeeDecorator:
def __init__(self, coffee):
self._coffee = coffee
def cost(self):
return self._coffee.cost()
class MilkDecorator(CoffeeDecorator):
def cost(self):
return self._coffee.cost() + 2
class SugarDecorator(CoffeeDecorator):
def cost(self):
return self._coffee.cost() + 1
coffee = Coffee()
coffee_with_milk = MilkDecorator(coffee)
coffee_with_milk_and_sugar = SugarDecorator(coffee_with_milk)
print(coffee_with_milk_and_sugar.cost()) # Output: 8