Decorator Design Pattern
{% raw %}
Decorator design pattern is used to modify the functionality of an object at runtime. At the same time other instances of the same class will not be affected by this, so individual object gets the modified behaviour.
When to use ?
- When you want to transparently and dynamically add responsibilities to objects without affecting other objects.
- When you want to add responsibilities to an object that you may want to change in future.
- Extending functionality by sub-classing is no longer practical.
- To Avoid class explosion
- If we anticipate the need to apply the same feature to multiple objects or reuse specific functionality across different parts of our application, decorators promote code reusability.
- Legacy code changes: The Decorator pattern can be applied to existing classes, even in legacy code, making it an excellent choice when we need to introduce new functionality without refactoring large portions of the codebase.
- Separation of Concerns: If we want to separate different concerns or aspects of an object’s functionality into distinct components for better organization and maintainability, the Decorator pattern helps achieve this separation.
When not to use ?
- Static and Simple Behavior: If an object’s behavior is simple and unlikely to change, and there is no need for dynamic extensions, using decorators might introduce unnecessary complexity.
- Performance Considerations: Adding multiple decorators to an object can introduce a slight performance overhead due to the method delegation involved. If performance is a critical concern, we should evaluate the impact carefully.
- Complexity: Overusing decorators can lead to a complex web of decorators, making the code harder to understand and maintain. In such cases, other design patterns or refactoring may be more appropriate.
How it’s coming in to the picture ?
We use inheritance or composition to extend the behaviour of an object but this is done at compile time and its applicable to all the instances of the class. We can’t add any new functionality of remove any existing behaviour at runtime – this is when Decorator pattern comes into picture.
Structure
- Component: Declares the common interface for both wrappers and the wrapped objects.
- Concrete Component: Class of objects being wrapped. It defines the basic behaviour, which can be altered by decorators.
- Concrete Decorators: Defines extra behaviours that can be added to components dynamically. Concrete decorators override methods of Base Decorators and execute their behaviour either before or after calling the parent method.
- Base Decorator: It has a field for referencing a wrapped object. The field’s type should be declared as the component interface. So it can contain both concrete components and decorators. The base decorator delegates all operations to the wrapped object.
- Client: It wrap components into multiple layers of decorators, as long as it works with all objects via the component interface.

Example: PIZZA Store

Consider you are opening a pizza store. You have different departments different pizzas. Currently you are providing FarmhousePizza and MargherittaPizza.
Consider some customers came and ordered FarmHousePizza with extra toppings of mushroom. You might think of creating a separate team for this. But this is not a good approach. Since there is a high chance of someone coming and ordering a pizza with different toppings. So we need to handle this in a decorative way!
Here we have the BasePizza class , which costs about 200 rs. Now if we want to add cheese or mushroom we can acheive the same using ToppingDecorator class like below,
from abc import ABC# Abstact class - Interface - Componentclass BasePizza(ABC): def cost(): pass# Concrete Componentclass FarmHousePizza(BasePizza): def cost(self): return 200# Concrete Componentclass MargheritaPizza(BasePizza): def cost(self): return 100# Base Decoratorclass ToppingDecorator(BasePizza): def cost(): pass# Concrete Decoratorclass ExtraCheese(ToppingDecorator): def __init__(self, pizza: BasePizza): self.base_pizza = pizza def cost(self): return self.base_pizza.cost() + 10class Mushroom(ToppingDecorator): def __init__(self, pizza: BasePizza): self.base_pizza = pizza def cost(self): return self.base_pizza.cost() + 20farmhouse_pizza = FarmHousePizza()final_pizza = Mushroom(farmhouse_pizza)final_pizza = ExtraCheese(Mushroom(farmhouse_pizza))print('Cost of final pizza', final_pizza.cost())Advantages
- It provides greater flexibility than static interfaces
- It enhances the extensibility of the object, because changes are made by coding new classes.
- It simplifies the coding by allowing you to develop a series of functionality from targeted classes instead of coding all of the behaviour into the object
Disadvantages
- Overuse of decorators can lead to a complex nesting.
- Increased number of classes
- Maintenance Overhead
- Performance Overhead – Due to method delegation
- Limited to interface inheritance – The Decorator pattern relies on interface inheritance, which means that it can only extend the behavior of objects through interfaces or abstract classes. It cannot add new data members to the original object.
- Limited support for removing decorators during runtime.
- In some cases Composite or Strategy Design pattern might be more efficient.
{% endraw %}