Understanding the Decorator Design Pattern in Java
The Decorator Design Pattern is a structural design pattern that allows behavior to be added to individual objects dynamically without affecting other objects from the same class. It works by wrapping the original object inside special wrapper objects that add the new behavior.
This approach is especially useful when there are many possible combinations of behaviors, and subclassing for each variation is not feasible.
What is the Decorator Design Pattern?
Decorator pattern lets you attach additional responsibilities to an object at runtime. Instead of creating many subclasses for every possible combination, decorators allow behaviors to be composed automatically in a flexible and reusable way.
Real-World Analogy: Pizza Toppings
Think about ordering a pizza. You start with a base pizza and then add various toppings like extra cheese, jalapenos, mushrooms, etc. Instead of creating a subclass for every topping combination, each topping acts as a decorator that wraps the pizza, adding its own behavior.
Key Components
- Component Interface: Declares the operations (e.g.,
bake()) that can be dynamically added to objects. - Concrete Component: The original object to which additional responsibilities can be added (e.g.,
BasePizza). - Decorator: Maintains a reference to a component object and defines an interface that conforms to the component's interface.
- Concrete Decorators: Extend the decorator and add extra behavior (e.g.,
CheeseDecorator,JalapenoDecorator).
Java Code Example
// Component Interface
public interface Pizza {
String bake();
}
// Concrete Component
public class BasePizza implements Pizza {
public String bake() {
return "Base Pizza";
}
}
// Decorator Abstract Class
public abstract class PizzaDecorator implements Pizza {
protected Pizza pizza;
public PizzaDecorator(Pizza pizza) {
this.pizza = pizza;
}
public String bake() {
return pizza.bake();
}
}
// Concrete Decorators
public class CheeseDecorator extends PizzaDecorator {
public CheeseDecorator(Pizza pizza) {
super(pizza);
}
public String bake() {
return super.bake() + " + Cheese";
}
}
public class JalapenoDecorator extends PizzaDecorator {
public JalapenoDecorator(Pizza pizza) {
super(pizza);
}
public String bake() {
return super.bake() + " + Jalapenos";
}
}
Using the Decorator Pattern
public class DecoratorPatternDemo {
public static void main(String[] args) {
Pizza basePizza = new BasePizza();
// Add cheese decoration
Pizza cheesePizza = new CheeseDecorator(basePizza);
// Add jalapeno decoration on top of cheese
Pizza fullyLoadedPizza = new JalapenoDecorator(cheesePizza);
System.out.println(fullyLoadedPizza.bake());
// Output: Base Pizza + Cheese + Jalapenos
}
}
Benefits of the Decorator Pattern
- Promotes flexibility by allowing behaviors to be added and removed at runtime.
- Avoids subclass explosion by combining behaviors dynamically.
- Follows the Open/Closed principle by extending behavior without modifying existing code.
Summary
The Decorator Design Pattern is a powerful structural pattern that adds functionality to objects at runtime in a flexible and reusable manner without creating large numbers of subclasses. It’s easy to implement and a favorite topic in design pattern interviews.
If this post helped understand the Decorator Pattern, leave your comments or questions below and follow for more Java design pattern tutorials.
Comments
Post a Comment