Sometimes we want to add responsibilities to individual objects, not to an entire class.

One way to add responsibilities is with inheritance. A more flexible approach is to enclose the component in another object that adds the required behavior. The enclosing object is called a decorator. The decorator conforms to  interface of the component it decorates so that its presence is transparent to the component’s clients. The decorator forwards requests to the wrapped object and may  perform additional actions before or after forwarding.

1. Intent

Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

2. Also Known As

Wrapper

3. Applicability

Use Decorator

  • to add responsibilities to individual objects dynamically and transparently, that is, without affecting other objects.
  • for responsibilities that can be withdrawn.
  • when extension by subclassing 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 maybe hidden or otherwise unavailable for subclassing.

4. How to Implement

Coffee shop abstraction will be used to demonstrate the decorator pattern.

We have Beverage class and several implementations. These beverages will be decorated with condiments. Condiment decorators enhance existing functionality when calculating cost and printing description of the coffee. Let’s start.

Beverage abstract class is the main class to work with.

/**
 * This is the base component class in decorator pattern.
 */
public abstract class Beverage {

    protected String description = "Unknown Beverage";

    /**
     * This will be reused for concrete components by changing only description variable, not with overriding. But for condiments code will
     * be changed
     */
    public String getDescription() {
        return description;
    }

    public abstract double cost();
}

 

Espresso and HouseBlend implement Beverage abstract class.

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;
    }
}

 

CondimentDecorator is the base class for condiments. Note that, it extends Beverage, so the clients can behave condiments as beverages. It also redefines getDescription() method as abstract.

public abstract class CondimentDecorator extends Beverage {

    public abstract String getDescription();
}

 

And we have several CondimentDecorator implementations.

Note that each condiment defines a Beverage as its instance field while extending Beverage class. Each CondimentDecorator calls wrapped Beverage before/after it has completed its operation. Remember that cost() and getDescription() are defined in Beverage class.

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";
    }
}

 

Sample invocation is follows:

public class ClientMain {

    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());

        b = new Espresso();
        System.out.println(b.getDescription());
        System.out.println(b.cost());
    }
}

Output:

House Blend, Whip, Mocha, Mocha
2.05
Espresso
1.0

 

5. Source Code

Source code can be found on Github.

 

Leave a Reply

Close Menu