1. The Problem: Class Explosion
Imagine a Coffee shop. You have a BaseCoffee class. You want to add Milk, Sugar, Mocha, and Whip.
- If you use inheritance, you need a class for every combination:
CoffeeWithMilk,CoffeeWithMilkAndSugar, etc. - This leads to hundreds of classes that are impossible to maintain.
2. The Solution: Decorator Pattern
A decorator "wraps" the original object. It has the same interface as the object it's decorating.
The Implementation
public abstract class CoffeeDecorator implements Coffee {
protected Coffee baseCoffee;
public CoffeeDecorator(Coffee coffee) {
this.baseCoffee = coffee;
}
}
public class MilkDecorator extends CoffeeDecorator {
public double getCost() {
return baseCoffee.getCost() + 0.50;
}
}
3. Real-world Intuition: The Winter Clothing
You are the "Base Object."
- You put on a Shirt (Decorator 1).
- You put on a Sweater (Decorator 2) over the shirt.
- You put on a Jacket (Decorator 3) over the sweater. Each layer adds "Warmth" (Behavior) without changing who you are.
4. Interview Discussion
- Trade-offs: Decorators can make it hard to identify the original object (it's buried under layers). They also result in many small objects.
- Java I/O Example:
new BufferedReader(new FileReader("file.txt"))is the most famous use of the Decorator pattern in the Java JDK.
Final Takeaway
Use the Decorator pattern when you need to add or remove behavior at Runtime.