Introduction

Decorator pattern allows a user to add new functionality to an existing object without altering its structure. This type of design pattern comes under structural pattern as this pattern acts as a wrapper to existing class. This pattern creates a decorator class which wraps the original class and provides additional functionality keeping class methods signature intact.

Intent

  • Add additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
  • Client-specified embellishment of a core object by recursively wrapping it.
  • Use composition to extend the behaviour of an object and adhere to Open – Close principal.

Implementation

Decorator Pattern UML
Decorator Pattern UML Diagram

The participants classes in the decorator pattern are:

  • Component – Interface for objects that can have responsibilities added to them dynamically.
  • ConcreteComponent – Defines an object to which additional responsibilities can be added.
  • Decorator – Maintains a reference to a Component object and defines an interface that conforms to Component’s interface.
  • Concrete Decorators – Concrete Decorators extend the functionality of the component by adding state or adding behaviour.

Example

Following given example is an implementation of decorator design pattern. Icecream is a classic example for decorator design pattern. You create a basic icecream and then add toppings to it as you prefer. You can add as many topping as you want.

public interface Icecream {
  public String makeIcecream();
}

public class SimpleIcecream implements Icecream {

  @Override
  public String makeIcecream() {
    return "Base IceCream";
  }
}

// Decorator
abstract class IcecreamDecorator implements Icecream {

  protected Icecream iceCream;

  public IcecreamDecorator(Icecream iceCream) {
    this.iceCream = iceCream;
  }

  public String makeIcecream() {
    return iceCream.makeIcecream();
  }
}

// Class implementing decorator
public class NuttyDecorator extends IcecreamDecorator {

  public NuttyDecorator(Icecream iceCream) {
    super(iceCream);
  }

  public String makeIcecream() {
    return iceCream.makeIcecream() + addNuts();
  }

  private String addNuts() {
    return " + cruncy nuts";
  }
}

public class HoneyDecorator extends IcecreamDecorator {

  public HoneyDecorator(Icecream iceCream) {
    super(iceCream);
  }

  public String makeIcecream() {
    return iceCream.makeIcecream() + addHoney();
  }

  private String addHoney() {
    return " + sweet honey";
  }
}


public class TestDecorator {

  public static void main(String args[]) {

    Icecream icecream = new SimpleIcecream();
    icecream = new NuttyDecorator(icecream);
    icecream = new HoneyDecorator();

    System.out.println(icecream.makeIcecream());
  }

}

Advantages

  • Decorator pattern can be used to make it possible to extend (decorate) the functionality of a certain object at runtime.
  • Decorator pattern is an alternative to subclassing. Subclassing adds behaviour at compile time, and the change affects all instances of the original class; decorating can provide new behaviour at runtime for individual objects
  • Instead of trying to support all foreseeable features in a complex, customizable class, one can define a simple class and add functionality incrementally with Decorator objects.