Other Tutorials

Decorator Design Pattern In Java

Introduction:

A decorator design pattern allows dynamically attaching the additional responsibilities or behaviors to an object at runtime. It is a structural pattern and makes use of aggregation to combine those behaviors.

In this tutorial, we’ll learn to implement the decorator pattern.

UML Diagram:

Let’s start by looking at the UML representation of a decorator pattern:Decorator Design Pattern

The ConcreteComponent class is the one for which we’ll like add additional behaviors at runtime. The ConcreteDecorator1, ConcreteDecorator2, … are the decorator classes which hold the logic to decorate a given Component.

Note that the abstract Decorator classhas a Component. In other words, it aggregates any other type of component which allows us to stack components one on the top of the other.

Moreover, both the ConcreteComponent and Decorator classes implement a common interface – Component.

Example Implementation:

Let’s say we are selling a gift item. Once a user selects a gift item, there can be multiple ways just to decorate that gift item say with a red or a blue ribbon, purple or green gift wrap, etc.

Rather than creating a class for each possible combination, it’s a good idea to implement it using a decorator pattern.

So, let’s create our GiftComponent interface:

public interface GiftComponent {
    void pack();
}

Also, let’s write our GiftItem class which is a concrete implementation of the GiftComponent:

public class GiftItem implements GiftComponent {

    public void pack() {
        System.out.println("Putting it in a box");
    }
}

Implementing Abstract Decorator:

Now that we have a GiftItem that we’ll like to decorate, let’s define our abstract GiftDecorator class:

public abstract AbstractGiftDecorator implements GiftComponent {
    protected GiftComponent gift;

    public AbstractGiftDecorator(GiftComponent gift) {
        this.gift = gift;
    }
    
    public void pack() {
        this.gift.pack();
    }
}

The gift decorator has a single instance of the gift component. This enables stacking decorators on top of one another.

Creating Multiple Decorators:

Finally, we can create as many custom decorators as we want.

Let’s create a few gift wraps:

public class PurpleWrapper extends AbstractGiftDecorator {

    public PurpleWrapper(GiftComponent gift) {
        super(gift);
    }

    public void pack() {
        super.pack();
        System.out.println("Purple wrapper");
    }
}

public class RedWrapper extends AbstractGiftDecorator {

    public RedWrapper(GiftComponent gift) {
        super(gift);
    }

    public void pack() {
        super.pack();
        System.out.println("Red wrapper");
    }
}

And a few types of ribbons for further decoration:

public class BlueRibbon extends AbstractDecorator {

    public BlueRibbon(GiftComponent gift) {
        super(gift);
    }

    public void pack() {
        super.pack();
        System.out.println("Blue ribbon");
    }
}

public class PinkRibbon extends AbstractDecorator {

    public PinkRibbon(GiftComponent gift) {
        super(gift);
    }

    public void pack() {
        super.pack();
        System.out.println("Pink Ribbon");
    }
}

Testing Our Implementation:

Let’s now test out our implementation to see what happens:

// client code
GiftComponent gift = new GiftItem();
GiftComponent giftWithPurpleWrapper = new PurpleWrapper(gift);
GiftComponent giftWithPurpleWrapperAndPinkRibbon =
  new PinkRibbon(giftWithPurpleWrapper);

giftWithPurpleWrapperAndPinkRibbon.pack();

As we can see, we are now able to easily and elegantly wrap the gift item in the way we want just by chaining the decorators. The above code will print:

Putting it in a box
Purple Wrapper
Pink Ribbon

Conclusion:

The decorator design pattern uses aggregation as a substitute for a pure inheritance. It allows us to dynamically add behaviors to an object. It takes away the overhead of creating a separate class for every possible combination thereby significantly reducing the number of classes.

Also, it adheres to the Single Responsibility Principle which states that every class must exactly do one thing. The classes like java.io.BufferedReader, java.io.FileReader are designed using the decorator design pattern.

One comment
Pingback: Decorator Design Pattern In Java

Leave a Comment

Your email address will not be published. Required fields are marked *