What if you could add new features anytime without rewriting your whole code?
Why Decorator pattern in LLD? - Purpose & Use Cases
Imagine you have a simple coffee order system. You want to add options like milk, sugar, or whipped cream. Without a smart design, you might create a new class for every combination, like CoffeeWithMilk, CoffeeWithSugar, CoffeeWithMilkAndSugar, and so on.
This manual way quickly becomes a mess. You end up with many classes, hard to maintain and extend. Adding a new option means creating even more classes. It's slow, confusing, and error-prone.
The Decorator pattern lets you add features dynamically by wrapping objects. Instead of many classes, you wrap your coffee with decorators like MilkDecorator or SugarDecorator. This keeps code simple, flexible, and easy to extend.
class CoffeeWithMilkAndSugar extends Coffee { ... }coffee = new SugarDecorator(new MilkDecorator(new SimpleCoffee()))
It enables flexible, reusable, and easy-to-maintain code by adding features without changing existing code.
Think of ordering a burger where you add toppings one by one. Each topping wraps the burger, adding flavor without changing the original burger.
Manual class explosion is hard to manage.
Decorator pattern wraps objects to add features dynamically.
It keeps code clean, flexible, and easy to extend.
Practice
Decorator pattern in system design?Solution
Step 1: Understand the Decorator pattern goal
The Decorator pattern is designed to add new behavior or features to objects dynamically without modifying their original code.Step 2: Compare options with the pattern goal
Options B, C, and D do not describe adding features without changing code. Only To add new features to objects without changing their original code matches the pattern's purpose.Final Answer:
To add new features to objects without changing their original code -> Option BQuick Check:
Decorator pattern = add features without code change [OK]
- Confusing Decorator with Factory pattern
- Thinking it replaces objects instead of wrapping
- Assuming it stores data instead of adding behavior
Solution
Step 1: Recall Decorator implementation style
Decorator pattern uses a wrapper class that holds an instance of the original class and adds new behavior.Step 2: Evaluate each option
Create a new class that wraps the original class and adds new methods correctly describes wrapping with a new class. Options A, B, and C do not follow the decorator pattern principles.Final Answer:
Create a new class that wraps the original class and adds new methods -> Option DQuick Check:
Decorator = wrapper class adding methods [OK]
- Changing original class instead of wrapping
- Replacing class instead of extending behavior
- Using global variables for decoration
class Coffee:
def cost(self):
return 5
class MilkDecorator:
def __init__(self, coffee):
self.coffee = coffee
def cost(self):
return self.coffee.cost() + 2
order = MilkDecorator(Coffee())
print(order.cost())What will be the output of
print(order.cost())?Solution
Step 1: Understand the base cost
TheCoffeeclass returns 5 whencost()is called.Step 2: Calculate decorated cost
TheMilkDecoratoradds 2 to the coffee cost by callingself.coffee.cost()and adding 2.Final Answer:
7 -> Option AQuick Check:
5 + 2 = 7 [OK]
- Forgetting to call wrapped object's method
- Returning only decorator's added cost
- Confusing base and added costs
class LoggerDecorator:
def __init__(self, component):
self.component = component
def execute(self):
print('Logging start')
self.execute()
print('Logging end')Solution
Step 1: Analyze method calls inside execute()
Theexecute()method callsself.execute(), which calls itself recursively without a base case.Step 2: Identify correct call to wrapped component
The decorator should callself.component.execute()to delegate the call, not call itself.Final Answer:
The decorator calls itself recursively causing infinite loop -> Option AQuick Check:
Recursive call without base case = infinite loop [OK]
- Calling self.execute() instead of wrapped component
- Missing delegation to wrapped object
- Ignoring recursion issues
Solution
Step 1: Identify need for dynamic feature addition
Encryption and logging should be added without modifying core notification classes, so dynamic wrapping is needed.Step 2: Apply Decorator pattern for flexible feature addition
Create decorator classes that wrap notification objects and add encryption or logging behavior dynamically.Final Answer:
Create decorator classes for encryption and logging that wrap notification objects -> Option CQuick Check:
Decorator pattern = dynamic wrapping for features [OK]
- Modifying core classes directly
- Using inheritance explosion for feature combos
- Ignoring dynamic behavior needs
Ready to prove it?
Pass the test and this pattern moves unknown
