Decorator Design Pattern

The Decorator Design Pattern adds additional functionalities dynamically to an object. A decorator extends the responsibilities of a class by using composition not inheritance.

decorator object contains an original object instance as member variable and it adds new functionalities either before or/and after delegating it to the original object. It comes under structural design pattern as it provides one of the best ways to extend the responsibilities of a class.

Advantages of Decorator Pattern

  • It allows classes to extend easily to support new requirements without modifying existing code.
  • A decorator implements the same interface as the object it decorates. We can use the decorator object(typecasted to supertype) where original object was expected.

When we should use Decorator Pattern

  • When we want to dynamically add or remove functionalities to a class without affecting the behaviour of other objects from the same class.
  • When we want to decouple Concrete implementations from responsibilities and behaviours.
  • We want to add additional functionalities to a class without affecting any of the clients.
  • When extending the functionalities of a class using inheritance will end up in creating lots of sub-classes.

Implementation of Decorator Design Pattern

In this example, we already have a PlainPizza class that implements Pizza interface. Now we want to add an extra functionality of adding extra cheese to pizza by keeping the interface same as Pizza.

Decorator_Pattern (1)

We will first create an Pizza interface and it’s concrete implementation PlainPizza.

Decorator Design Pattern

Pizza.java
public interface Pizza {
    public float getPrice();
    public void preparePizza();
    public void packPizza();
}
PlainPizza.java
public class PlainPizza implements Pizza {
    @Override
    public float getPrice(){
        return 10;
    }
  
    @Override 
    public void preparePizza(){
        System.out.println("Prepairing Pizza");
    }
  
    @Override
    public void packPizza(){
        System.out.println("Packing Pizza");
    }
}

Create abstract PizzaDecorator class implementing the Pizza interface and concrete decorator class extending PizzaDecorator class. This will ensure that all decorator classes will have common Pizza interface.

Decorator Design Pattern 1

PizzaDecorator.java
public abstract class PizzaDecorator implements Pizza {
  
    protected Pizza pizza;
  
    public PizzaDecorator(Pizza pizza) {
        this.pizza = pizza;
    }
  
    public float getPrice(){
        return pizza.getPrice();
    }
  
    public void preparePizza(){
        pizza.preparePizza();
    }
  
    public void packPizza(){
        pizza.packPizza();
    }
}

CheeseBurstPizza.java class overrides the Pizza interface methods to provide additional functionalities or adding more cheese toppings.

Decorator Design Pattern 2

CheeseBurstPizza.java
public class CheeseBurstPizza extends PizzaDecorator {
  
    public CheeseBurstPizza(Pizza pizza){
        super(pizza);
    }
  
    @Override
    public float getPrice(){
        // Extra money for Cheese Burst 
        return pizza.getPrice() + 5.0f;
    }
  
    @Override
    public void preparePizza(){
        System.out.println("Adding Extra Cheese..");
        pizza.preparePizza();
    }
  
    @Override
    public void packPizza(){
        pizza.packPizza();
    }
}

DecoratorPatternExample.java class creates a plane pizza and Cheese Burst Pizza using Pizza interface.

Decorator Design Pattern

DecoratorPatternExample.java
public class DecoratorPatternExample {
  public static void main(String[] args) { 
      // Prepairing a Plain pizza
      Pizza plainPizza = new PlainPizza();
      plainPizza.preparePizza();
      plainPizza.packPizza();
      System.out.println(plainPizza.getPrice());
      System.out.println("---------------------------");
    
      // Prepairing a Cheese Burst Pizza using Pizza interface only
      Pizza cheeseBurstPizza = new CheeseBurstPizza(new PlainPizza());
      cheeseBurstPizza.preparePizza();
      cheeseBurstPizza.packPizza();
      System.out.println(cheeseBurstPizza.getPrice());
  }
}

Output

Prepairing Pizza
Packing Pizza
10.0
---------------------------
Adding Extra Cheese..
Prepairing Pizza
Packing Pizza
15.0