1. Overview

There are mainly two ways to add new responsibility to a class: inheritance and composition. With inheritance, we rely on the subclasses for the required behavior. With composition, on the other hand, we rely on the composed objects to combine their functionalities and create the required behavior. In this tutorial, we'll look at how we can implement the decorator pattern in Java. It's also known as the wrapper pattern. In essence, we'll enclose a component in another object that adds the required behavior. The enclosing object is called a decorator. The decorator implements the interface of the component it decorates so that its presence is transparent to the clients. The decorator forwards the requests to the wrapped object and may perform additional actions.

2. Motives

Let's talk more about the motives for applying the decorator pattern.

Firstly, the composition aspect allows us to add new functionalities dynamically. For example, we can create a logging decorator around an application service, then further decorate it for the transaction support. Then we can add a final layer with a caching decorator. This flexibility comes from the fact that decorators can be combined in any way possible. Conversely, it can produce an explosion of subclasses to support the same level of combinations.

Secondly, we know that decorators implement the interface of the wrapped component. This means that the clients won't notice whether they're interacting with a decorator or with the actual component. This enables us to modify the object hierarchy without affecting the clients.

Also, decorators focus on a single responsibility. While they don't know the details of the other decorators or implementations, they also don't make any assumptions about them. This leads to a loosely-coupled design.

Lastly, subclassing may not be an option in some cases. For example, the class may be final or not visible for inheritance.

3. How to Implement

We'll use the coffee shop abstraction to demonstrate the decorator pattern.

We have the Beverage class and several implementations. We'll decorate these beverages with condiments. The condiment decorators enhance the existing functionality when calculating the cost and printing the coffee description.

The Beverage class is the main class that we'll work with:

public abstract class Beverage {

    protected String description = "Unknown Beverage";

    public String getDescription() {
        return description;
    }

    public abstract double cost();
}

All subclasses must implement the abstract cost method.

We'll next provide some implementations:

public class Espresso extends Beverage {

    public Espresso() {
        this.description = "Espresso";
    }

    @Override
    public double cost() {
        return 1;
    }
}
public class HouseBlend extends Beverage {

    public HouseBlend() {
        this.description = "House Blend";
    }

    @Override
    public double cost() {
        return 1.5;
    }
}

Here, Espresso and HouseBlend extend the Beverage abstract class. They also implement the cost method and update the description.

After covering the actual beverages, we'll next look at CondimentDecorator that is the base class for condiments:

public abstract class CondimentDecorator extends Beverage {

    public abstract String getDescription();
}

Note that it extends Beverage, so the clients can behave condiments as beverages. It also redefines the getDescription method as abstract.

Then we'll create several CondimentDecorator implementations:

public class Mocha extends CondimentDecorator {

    private final Beverage beverage;

    public Mocha(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public double cost() {
        return 0.20 + beverage.cost();
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Mocha";
    }
}

public class Whip extends CondimentDecorator {

    private final Beverage beverage;

    public Whip(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public double cost() {
        return 0.15 + beverage.cost();
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Whip";
    }
}

Notice the instance variables. Each condiment contains a Beverage instance while also extending the Beverage class. Additionally, each CondimentDecorator calls the wrapped Beverage before/after completing its operation.

Lastly, we have a test application:

public static void main(String[] args) {
    Beverage b = new Mocha(new Mocha(new Whip(new HouseBlend())));
    System.out.println(b.getDescription());
    System.out.println(b.cost());
}

Here, we're decorating a HouseBlend instance with a Whip first. Then we're creating two Mocha decorators around it.

A sample run prints:

House Blend, Whip, Mocha, Mocha
2.05

4. Summary

In this tutorial, we've investigated how we can implement the decorator pattern in Java.

Check out the source code for the examples over on Github.