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
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.