DECORATOR PATTERN

The intent of the Decorator Design Pattern is to attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to sub-classing for extending functionality.

The Decorator Pattern is used to extend the functionality of an object dynamically without having to change the original class source or using inheritance. This is accomplished by creating an object wrapper referred to as a Decorator around the actual object.

The Decorator object is designed to have the same interface as the underlying object. This allows a client object to interact with the Decorator object in exactly the same manner as it would with the underlying actual object. The Decorator object contains a reference to the actual object. The Decorator object receives all requests (calls) from a client. In turn, it forwards these calls to the underlying object. The Decorator object adds some additional functionality before or after forwarding requests to the underlying object. This ensures that the additional functionality can be added to a given object externally at runtime without modifying its structure.

We use inheritance or composition to extend the behavior of an object but this is done at compile time and it’s applicable to all the instances of the class. We can’t add any new functionality of remove any existing behavior at runtime – this is when Decorator pattern comes into picture.

Component Interface – The interface or abstract class defining the methods that will be implemented. In our case Icecream will be the component interface.

package com.myjavablog.structural.decorator;

public interface Icecream {

public String makeIcecream();
}

Component Implementation – The basic implementation of the component interface. We can have SimpleIcecream class as our component implementation.

package com.myjavablog.structural.decorator;

public class SimpleIcecream implements Icecream {

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

Decorator – Decorator class implements the component interface and it has a HAS-A relationship with the component interface. The component variable should be accessible to the child decorator classes, so we will make this variable protected.

package com.myjavablog.structural.decorator;

public class IcecreamDecorator implements Icecream {

protected Icecream specialIcecream;

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

@Override
public String makeIcecream() {
return specialIcecream.makeIcecream();
}
}

Concrete Decorators – Extending the base decorator functionality and modifying the component behavior accordingly. We can have concrete decorator classes as NuttyIcecreamDecorator & HoneyIcecreamDecorator.

package com.myjavablog.structural.decorator;

public class NuttyDecorator extends IcecreamDecorator {

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

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

public String addNuts(){
return "+ Crunchy Nuts ";
}
}

package com.myjavablog.structural.decorator;

public class HoneyDecorator extends IcecreamDecorator {

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

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

public String addHoney() {
return "+ Honey sweetner ";
}
}

 

package com.myjavablog.structural.decorator;

public class DecoratorPatternTest {

public static void main(String[] args) {

Icecream icecream = new HoneyDecorator(new NuttyDecorator(new SimpleIcecream()));
System.out.println(icecream.makeIcecream());
}
}

Output:

Base Icecream + Crunchy Nuts + Honey sweetner

When to use the Decorator Design Pattern:

Use the Decorator pattern in the following cases:

  • To add responsibilities to individual objects dynamically and transparently, that is, without affecting other objects.
  • For responsibilities that can be withdrawn.
  • When extension by sub-classing is impractical. Sometimes a large number of independent extensions are possible and would produce an explosion of subclasses to support every combination. Or a class definition may be hidden or otherwise unavailable for sub-classing.
  • It’s easy to maintain and extend when the number of choices are more.

Usage in Java:

  • Decorator pattern is used a lot in Java IO classes, such as FileReader, BufferedReader etc.
  • The disadvantage of decorator pattern is that it uses a lot of similar kind of objects (decorators).

Leave a Comment

Bitnami