00:00

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.