The Decorator Pattern is a structural design pattern that allows behavior to be added to individual objects dynamically without affecting the behavior of other objects from the same class. It's often used to extend functionality in a flexible and reusable way.
Key Concepts 📌
- Purpose: Attach additional responsibilities to an object dynamically.
- Use Case: Adding features to objects in a transparent and efficient manner.
- Implementation: Uses composition instead of inheritance.
When to Use? 🎯
- You need to add responsibilities to objects runtime.
- You want to avoid a class explosion (many subclasses for small modifications).
- You need to conserve the original object's interface.
Example Code 🧾
class Coffee:
def cost(self):
return 2.0
class MilkDecorator:
def __init__(self, coffee):
self._coffee = coffee
def cost(self):
return self._coffee.cost() + 0.5
class SugarDecorator:
def __init__(self, coffee):
self._coffee = coffee
def cost(self):
return self._coffee.cost() + 0.2
# Usage
basic_coffee = Coffee()
print(f"Cost of basic coffee: ${basic_coffee.cost()}") # Output: $2.0
milk_coffee = MilkDecorator(basic_coffee)
print(f"Cost of milk coffee: ${milk_coffee.cost()}") # Output: $2.5
sugar_milk_coffee = SugarDecorator(milk_coffee)
print(f"Cost of sugar + milk coffee: ${sugar_milk_coffee.cost()}") # Output: $2.7
Benefits ✅
- Flexibility: Add or remove features dynamically.
- Reusability: Decorators can be reused across different objects.
- Extensibility: No need to modify existing code.
Related Reading 📚
For deeper insights into Python's decorator pattern, check out our Design Patterns in Python guide. It covers other patterns like Strategy and Proxy that complement decorators in real-world scenarios.