π§© Decorator Design Pattern in Java
The Decorator Pattern is a structural design pattern that allows you to add new behavior to an object without modifying its code.
π Real-Life Analogy
Think of a pizza shop:
- Base pizza = plain
- You can add cheese, veggies, or paneer without modifying the original pizza.
This is what Decorator Pattern does: it wraps an object to add new behavior.
π― When to Use It?
- When you want to add features to objects at runtime.
- When using inheritance would lead to a large number of subclasses.
- When you need to follow the Open/Closed Principle (Open for extension, Closed for modification).
π§ Example β Pizza Toppings
Letβs build a system where we can decorate a basic pizza with toppings.
πΈ Step 1: Create a Base Interface
public interface Pizza {
String getDescription();
double getCost();
}
πΈ Step 2: Concrete Component
public class Margherita implements Pizza {
public String getDescription() {
return "Margherita";
}
public double getCost() {
return 100.0;
}
}
πΈ Step 3: Abstract Decorator
public abstract class ToppingDecorator implements Pizza {
protected Pizza pizza;
public ToppingDecorator(Pizza pizza) {
this.pizza = pizza;
}
public String getDescription() {
return pizza.getDescription();
}
public double getCost() {
return pizza.getCost();
}
}
πΈ Step 4: Concrete Decorators (Toppings)
public class Cheese extends ToppingDecorator {
public Cheese(Pizza pizza) {
super(pizza);
}
public String getDescription() {
return super.getDescription() + ", Cheese";
}
public double getCost() {
return super.getCost() + 30.0;
}
}
public class Olives extends ToppingDecorator {
public Olives(Pizza pizza) {
super(pizza);
}
public String getDescription() {
return super.getDescription() + ", Olives";
}
public double getCost() {
return super.getCost() + 20.0;
}
}
πΈ Step 5: Test the Pizza Builder
public class DecoratorDemo {
public static void main(String[] args) {
Pizza pizza = new Margherita();
pizza = new Cheese(pizza);
pizza = new Olives(pizza);
System.out.println("Pizza: " + pizza.getDescription());
System.out.println("Total Cost: βΉ" + pizza.getCost());
}
}
β Output
Pizza: Margherita, Cheese, Olives
Total Cost: βΉ150.0
π Key Concepts
| Concept | Description | | ----------------- | ------------------------------------------------------ | | Component | Base interface or class (e.g., Pizza) | | ConcreteComponent | Actual implementation (e.g., Margherita) | | Decorator | Wraps component to add behavior (e.g., Cheese, Olives) |
π Real-World Use Cases
- β
Java I/O streams:
BufferedInputStream
,DataInputStream
- β Spring Security Filters
- β UI frameworks (adding scrollbars, borders to components)
- β Logging enhancements
π§ Interview Q&A
Q: What type of pattern is Decorator? A: Structural
Q: What's the difference between Decorator and Inheritance? A:
- Inheritance is compile-time and static
- Decorator is runtime and flexible
Q: Can we use multiple decorators? A: Yes, decorators can be stacked/wrapped as many times as needed.
π‘ Decorator vs Strategy vs Adapter
| Pattern | Purpose | | --------- | -------------------------------- | | Decorator | Add responsibilities dynamically | | Strategy | Select algorithm at runtime | | Adapter | Convert one interface to another |
π§ SOLID Principle in Play
- O β Open/Closed Principle: Easily extend object behavior without modifying their code.
π Summary
- β Decorator adds new behavior to objects without altering their structure.
- π§± Follows composition over inheritance.
- π οΈ Helps write flexible, reusable, and extensible code.
- βοΈ Used in Java libraries like I/O and GUI toolkits.