Decorator Pattern
Java Decorator Pattern (BEHAVIORAL PATTERN)
Adds behavior dynamically to objects without changing their class (wrapper pattern).
Decorator Pattern: Runtime wrapper that adds new behavior to objects - like adding toppings to pizza where each topping enhances what you already have!
2. Real-World Analogy
Section titled “2. Real-World Analogy”Pizza Toppings:
- Start with basic pizza
- Add cheese (extra cost + description)
- Add pepperoni (extra cost + description)
- Add mushrooms (extra cost + description)
- Each topping “decorates” the pizza
3. Simple Example: Without vs With Decorator
Section titled “3. Simple Example: Without vs With Decorator”❌ Without Decorator (Class Explosion)
Section titled “❌ Without Decorator (Class Explosion)”// Need separate class for every combination!class PlainPizza { double cost() { return 5.0; } }class CheesePizza { double cost() { return 6.0; } }class PepperoniPizza { double cost() { return 6.5; } }class CheesePepperoniPizza { double cost() { return 7.5; } }// ❌ 2^n classes for n toppings!// ❌ Adding new topping = 2^n new classes!✅ With Decorator (Flexible)
Section titled “✅ With Decorator (Flexible)”// Step 1: Component Interfaceinterface Pizza { double getCost(); String getDescription();}
// Step 2: Concrete Component (Base object)class PlainPizza implements Pizza { public double getCost() { return 5.0; // Base price }
public String getDescription() { return "Plain pizza"; }}
// Step 3: Base Decorator (Abstract)abstract class PizzaDecorator implements Pizza { protected Pizza pizza; // Wrapped pizza
public PizzaDecorator(Pizza pizza) { this.pizza = pizza; }
public double getCost() { return pizza.getCost(); }
public String getDescription() { return pizza.getDescription(); }}
// Step 4: Concrete Decorators (Toppings)class CheeseDecorator extends PizzaDecorator { public CheeseDecorator(Pizza pizza) { super(pizza); }
public double getCost() { return pizza.getCost() + 1.0; // Add cheese cost }
public String getDescription() { return pizza.getDescription() + ", Cheese"; // Add cheese description }}
class PepperoniDecorator extends PizzaDecorator { public PepperoniDecorator(Pizza pizza) { super(pizza); }
public double getCost() { return pizza.getCost() + 1.5; // Add pepperoni cost }
public String getDescription() { return pizza.getDescription() + ", Pepperoni"; // Add pepperoni }}
class MushroomDecorator extends PizzaDecorator { public MushroomDecorator(Pizza pizza) { super(pizza); }
public double getCost() { return pizza.getCost() + 0.75; // Add mushroom cost }
public String getDescription() { return pizza.getDescription() + ", Mushrooms"; // Add mushrooms }}
// Usagepublic class Main { public static void main(String[] args) { // Start with plain pizza Pizza pizza = new PlainPizza(); System.out.println(pizza.getDescription() + " $" + pizza.getCost()); // Output: Plain pizza $5.0
// Add cheese pizza = new CheeseDecorator(pizza); System.out.println(pizza.getDescription() + " $" + pizza.getCost()); // Output: Plain pizza, Cheese $6.0
// Add pepperoni pizza = new PepperoniDecorator(pizza); System.out.println(pizza.getDescription() + " $" + pizza.getCost()); // Output: Plain pizza, Cheese, Pepperoni $7.5
// Add mushrooms pizza = new MushroomDecorator(pizza); System.out.println(pizza.getDescription() + " $" + pizza.getCost()); // Output: Plain pizza, Cheese, Pepperoni, Mushrooms $8.25
// Can combine in any order! Pizza anotherPizza = new MushroomDecorator( new CheeseDecorator( new PlainPizza())); // Mushrooms + Cheese pizza! }}4. Real-World Examples
Section titled “4. Real-World Examples”Example 1: Coffee Shop
Section titled “Example 1: Coffee Shop”interface Coffee { double getCost(); String getDescription();}
class SimpleCoffee implements Coffee { public double getCost() { return 2.0; } public String getDescription() { return "Simple coffee"; }}
abstract class CoffeeDecorator implements Coffee { protected Coffee coffee; public CoffeeDecorator(Coffee coffee) { this.coffee = coffee; }}
class MilkDecorator extends CoffeeDecorator { public MilkDecorator(Coffee coffee) { super(coffee); } public double getCost() { return coffee.getCost() + 0.5; } public String getDescription() { return coffee.getDescription() + ", Milk"; }}
class SugarDecorator extends CoffeeDecorator { public SugarDecorator(Coffee coffee) { super(coffee); } public double getCost() { return coffee.getCost() + 0.2; } public String getDescription() { return coffee.getDescription() + ", Sugar"; }}
class WhippedCreamDecorator extends CoffeeDecorator { public WhippedCreamDecorator(Coffee coffee) { super(coffee); } public double getCost() { return coffee.getCost() + 1.0; } public String getDescription() { return coffee.getDescription() + ", Whipped Cream"; }}
// UsageCoffee coffee = new SimpleCoffee();coffee = new MilkDecorator(coffee);coffee = new SugarDecorator(coffee);coffee = new WhippedCreamDecorator(coffee);System.out.println(coffee.getDescription() + " $" + coffee.getCost());// Output: Simple coffee, Milk, Sugar, Whipped Cream $3.7Example 2: File I/O Decorators
Section titled “Example 2: File I/O Decorators”// JDK's InputStream uses Decorator pattern!FileInputStream fis = new FileInputStream("file.txt");BufferedInputStream bis = new BufferedInputStream(fis); // Adds bufferingDataInputStream dis = new DataInputStream(bis); // Adds data reading methods
// Each decorator adds functionality// FileInputStream → BufferedInputStream → DataInputStream5. Java I/O: The Best Example
Section titled “5. Java I/O: The Best Example”// java.io package is full of decoratorsInputStream in = new FileInputStream("data.txt");in = new BufferedInputStream(in); // Adds bufferingin = new DataInputStream(in); // Adds readInt(), readDouble(), etc.in = new PushbackInputStream(in); // Adds unread() capability
// Each layer adds new functionality!6. Interview Q&A
Section titled “6. Interview Q&A”Q1: Decorator vs Inheritance?
Section titled “Q1: Decorator vs Inheritance?”A:
- Inheritance: Adds behavior at compile-time (static)
- Decorator: Adds behavior at runtime (dynamic)
Q2: Decorator vs Strategy?
Section titled “Q2: Decorator vs Strategy?”A:
- Decorator: Enhances existing behavior (wrapping)
- Strategy: Replaces entire algorithm (swapping)
Q3: Decorator vs Proxy?
Section titled “Q3: Decorator vs Proxy?”A:
- Decorator: Adds new behavior
- Proxy: Controls access to object (may add some behavior)
Q4: Advantages?
Section titled “Q4: Advantages?”A:
- Flexible - combine behaviors at runtime
- Open/Closed - add new decorators without modifying code
- Single Responsibility - each decorator does one thing
- Avoids class explosion - no need for every combination
Q5: Disadvantages?
Section titled “Q5: Disadvantages?”A:
- Many small objects - can be hard to debug
- Complex initialization - many wrapper layers
- Order matters - different order = different behavior
Q6: Real examples in JDK?
Section titled “Q6: Real examples in JDK?”A:
java.iopackage (BufferedInputStream,DataInputStream)java.util.Collections(synchronizedList(),unmodifiableList())javax.servlet.http.HttpServletRequestWrapper
7. Implementation Template
Section titled “7. Implementation Template”// 1. Component Interfaceinterface Component { void operation();}
// 2. Concrete Componentclass ConcreteComponent implements Component { public void operation() { System.out.println("Basic operation"); }}
// 3. Base Decoratorabstract class Decorator implements Component { protected Component component;
public Decorator(Component component) { this.component = component; }
public void operation() { component.operation(); }}
// 4. Concrete Decoratorsclass ConcreteDecoratorA extends Decorator { public ConcreteDecoratorA(Component component) { super(component); }
public void operation() { super.operation(); addedBehavior(); }
private void addedBehavior() { System.out.println("Added behavior A"); }}
class ConcreteDecoratorB extends Decorator { public ConcreteDecoratorB(Component component) { super(component); }
public void operation() { super.operation(); addedBehavior(); }
private void addedBehavior() { System.out.println("Added behavior B"); }}
// 5. UsageComponent component = new ConcreteComponent();component = new ConcreteDecoratorA(component);component = new ConcreteDecoratorB(component);component.operation();// Output:// Basic operation// Added behavior A// Added behavior B8. Spring Framework Example
Section titled “8. Spring Framework Example”// Request/Response wrappers in Spring MVC@Componentclass LoggingDecorator extends HttpServletRequestWrapper {
public LoggingDecorator(HttpServletRequest request) { super(request); }
@Override public String getParameter(String name) { String value = super.getParameter(name); // Add logging behavior System.out.println("Parameter " + name + " = " + value); return value; }}
// Usage in filter@Componentpublic class LoggingFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { // Wrap request with decorator HttpServletRequest wrappedRequest = new LoggingDecorator(request); chain.doFilter(wrappedRequest, response); }}9. Modern Java Example
Section titled “9. Modern Java Example”// Using composition with Java 8+interface Text { String content();}
class PlainText implements Text { private String text; public PlainText(String text) { this.text = text; } public String content() { return text; }}
// Functional decoratorclass TextDecorator implements Text { private Text text; private Function<String, String> decorator;
public TextDecorator(Text text, Function<String, String> decorator) { this.text = text; this.decorator = decorator; }
public String content() { return decorator.apply(text.content()); }}
// UsageText text = new PlainText("Hello World");text = new TextDecorator(text, String::toUpperCase);text = new TextDecorator(text, s -> "*** " + s + " ***");System.out.println(text.content()); // *** HELLO WORLD ***10. Best Practices
Section titled “10. Best Practices”- Keep decorators lightweight - they should add minimal overhead
- Ensure decorators are transparent - client shouldn’t know it’s decorated
- Document decorator order if it matters
- Consider builder pattern for complex decoration chains