The Decorator pattern lets you add new behavior to individual objects without changing their original structure. It supports the Open/Closed Principle, allowing classes to be extended without being modified.
If you want to enhance an object’s behavior, one approach is to use inheritance. But this can lead to a large number of subclasses to cover all combinations of features. The Decorator pattern solves this by wrapping objects in other objects (decorators) that add or modify behavior, allowing you to build features dynamically and flexibly.
Use this pattern when:
Typical use cases include logging, input validation, user interface enhancements, or formatting.
Avoid this pattern if:
The pattern includes four main parts:
You can stack multiple decorators to build up the desired behavior.
Think of customizing a pizza. You start with a base (like Margherita), then add toppings such as mozzarella, olives, or mushrooms. Each topping adds a feature, but you don’t need a separate class for every topping combination—just layer decorators.
Here’s a basic Python example:
# Component interface
class Pizza:
def get_cost(self):
pass
def get_description(self):
pass
# Concrete component
class Margherita(Pizza):
def get_cost(self):
return 10
def get_description(self):
return "Margherita"
# Base decorator
class PizzaDecorator(Pizza):
def __init__(self, pizza: Pizza):
self.pizza = pizza
def get_cost(self):
return self.pizza.get_cost()
def get_description(self):
return self.pizza.get_description()
# Concrete decorator
class Mozzarella(PizzaDecorator):
def get_cost(self):
return super().get_cost() + 5
def get_description(self):
return f"{super().get_description()}, Mozzarella"
pizza = Mozzarella(Margherita())
print(pizza.get_description()) # Output: Margherita, Mozzarella
print(pizza.get_cost()) # Output: 15
You can stack decorators like Olives
, Mushrooms
, and so on, to build complex combinations.
View the complete implementation here: Decorator Pattern on GitHub