Decorator Design Pattern
The Decorator Design Pattern is a structural design pattern used to add new features or behavior to an object without changing its existing code. It works by wrapping the original object inside another object called a decorator.
This pattern follows the principle of “Open for extension, but closed for modification”. That means you can add new functionality without touching the original class.
Why Use the Decorator Pattern?
- To add responsibilities to objects at runtime
- To avoid creating too many subclasses
- To keep code flexible and easy to maintain
Real-Life Example
Think about ordering coffee at a café:
- Basic coffee ☕
- Add milk 🥛
- Add sugar 🍬
- Add whipped cream 🍦
Instead of creating separate classes like MilkCoffee, SugarCoffee, or MilkSugarCoffee, we can use the Decorator Pattern to add these extras dynamically.
Decorator Pattern Structure
- Component – Common interface
- Concrete Component – Basic implementation
- Decorator – Wraps the component
- Concrete Decorator – Adds new behavior
Java Example: Coffee Customization
1. Component Interface
public interface Coffee {
String getDescription();
double getCost();
}
2. Concrete Component
public class SimpleCoffee implements Coffee {
@Override
public String getDescription() {
return "Simple Coffee";
}
@Override
public double getCost() {
return 50.0;
}
}
3. Abstract Decorator
public abstract class CoffeeDecorator implements Coffee {
protected Coffee coffee;
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
}
4. Concrete Decorators
Milk Decorator
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription() + ", Milk";
}
@Override
public double getCost() {
return coffee.getCost() + 20.0;
}
}
Sugar Decorator
public class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription() + ", Sugar";
}
@Override
public double getCost() {
return coffee.getCost() + 10.0;
}
}
5. Client Code
public class DecoratorPatternDemo {
public static void main(String[] args) {
Coffee coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);
System.out.println(coffee.getDescription());
System.out.println("Total Cost: ₹" + coffee.getCost());
}
}
Output
Simple Coffee, Milk, Sugar Total Cost: ₹80.0
Key Advantages
- No changes required in existing classes
- More flexible than inheritance
- Follows clean design principles
When to Use the Decorator Pattern?
- When you need to add features dynamically
- When subclassing would create too many classes
- When behavior should be optional and combinable
Conclusion
The Decorator Design Pattern is a powerful way to enhance object behavior at runtime. It keeps your code clean, modular, and easy to extend. This makes it a popular choice in real-world Java applications and frameworks.