Your Guide to Design Patterns – Decorator Pattern

Design Patterns: Decorator Pattern

In the last few weeks we’ve looked at three creational patterns and one behavioural patterns. This week we’ll cover a commonly used structural pattern, the Decorator pattern (as known as the Wrapper pattern).

The Decorator Pattern

The Gang of Four book defines the Decorator pattern as follows:

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

If we want to extend the behaviour of a class we usually use inheritance. This is done at compile-time and it applies to all the instances of the class. However, we can’t use inheritance to add new functionality to an object at runtime – this is where the Decorator pattern comes into the picture.

The Decorator pattern can be used to extend (decorate) the functionality of an individual object (not the class itself). This can be done statically at compile-time or dynamically at run-time. This is done independently of other instances of the same class. Only individual objects will get the modified behaviour; other instances of the same class will not be affected by it.

Decorators provide a flexible alternative to subclassing for extending functionality. We can add new functionality to an existing object without altering its structure or changing the original class.

Setting the Stage

Back in our first person shooter game, we’ve decided that players should have the option to upgrade their vehicles with heavier armour, more and better weapons, bigger fuel tanks, etc.

We could use composition in an AbstractVehicle class to achieve this. We would add a Collection field for each upgrade option:

public abstract class AbstractVehicle {

    // fields for composition
    private Collection  armourLayers;
    private Collection  weapons;
    private Collection  fuelTanks;

    // other code

} // end of class

But what happens if a player decides against armour and weapons because they just want an ultra fast car? We’d have to do a lot of null checks on our fields (or use the NullObject pattern and code a few additional classes).

We could use inheritance to extend the behaviour of the AbstractVehicle class. A problem with this approach is that it creates a compile-time relationship between the added features and the extended classes. This leads to a very rigid design.

If we use inheritance, we will have to think of all the possible feature combinations and code them as separate classes. Imagine we had three Vehicle subclasses: BattleTankChaseCar and ArmouredCar. Let’s say we limit the players to three optional upgrades: ExtraArmourExtraWeapons and ExtraFuelTanks. We’d have to code twenty four (3 x 2 ^ 3) different class configurations! And our class hierarchy would exponentially expand with each additional upgrade we decide to add later. That’s scary enough to make us give up programming altogether!

So what should we do? Decorator to the rescue!

Imagine that we could code a “skin” (or wrapper) for each upgrade. These skins are decorator objects. As a player chooses an upgrade, an extra decorator object is wrapped around the vehicle. The decorator object implements the same interface as the object it decorates. New features are added before, after or in place of delegating requests to the wrapped object.

The decorator pattern contains four components:

  1. Component interface: This defines an interface to execute particular operations. In our game, this would be our AbstractVehicle.
  2. ConcreteComponent: This implements the operations defined in the Component interface. These would be our BattleTank and ChaseCar classes.
  3. AbstractDecorator: This is an abstract class that both extends the Component interface and contains a Component object. In the absence of this class, we need many subclasses of ConcreteDecorators for different combinations. Have the decorate contain a Component object (i.e. composition) reduces unnecessary subclasses. We’d write a VehicleDecorator class for this.
  4. ConcreteDecorator: These are the various implementations of AbstractDecorator. The sky’s the limit here – WeaponsDecoratorArmourDecorator, etc.

Decorator Pattern: Example Code

Here is the example code:

/*
Decorator example code:
    1. AbstractVehicle is the component interface. It defines an abstract method: decorateVehicle()
    2. BattleTank and ChaseCar are concrete implementations of AbstractVehicle.
    3. VehicleDecorator is an abstract class which contains AbstractVehicle.
    4. ArmourDecorator and WeaponsDecorator are concrete VehicleDecorator classes.

Obviously some of the methods are somewhat contrived; there would be more code in the constructors.
*/

public abstract class AbstractVehicle {
    protected String name;
    protected int weight; // tonnes

    public AbstractVehicle() {
    }
    public AbstractVehicle(String name){
        this.name = name;
    }
    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
    protected void setWeight(int weight){
        this.weight = weight;
    }
    protected int getWeight(){
        return weight;
    }
    protected abstract void decorateVehicle();
} // end of class

// ------------------------------------------------------------------

public class BattleTank extends AbstractVehicle {
    public BattleTank(String name){
        super(name);
        setWeight(10);
    }
    @Override
    public void decorateVehicle(){
        System.out.println("Weight of " + name + ": " + weight);
        // More functionality here...
    }
} // end of class

// ------------------------------------------------------------------

public class ChaseCar extends AbstractVehicle {
    public ChaseCar(String name){
        super(name);
        setWeight(2);
    }
    public void decorateVehicle(){
        System.out.println("Weight of " + name + ": " + weight);
        // More functionality here...
    }
} // end of class

// ------------------------------------------------------------------

public abstract class VehicleDecorator extends AbstractVehicle {
    protected AbstractVehicle vehicle;

    public VehicleDecorator(AbstractVehicle vehicle){
        this.vehicle = vehicle;
        setName(vehicle.getName() + " + " + getDecoratedName());
        setWeight(vehicle.getWeight() + getExtraWeight());
    }
    public void decorateVehicle(){
        vehicle.decorateVehicle();
        System.out.println("Weight of " + getName() + ": " + getWeight());
    }
    // abstract methods
    public abstract int getExtraWeight();
    public abstract String getDecoratedName();
} // end of class

// ------------------------------------------------------------------

public class WeaponsDecorator extends VehicleDecorator {
    public WeaponsDecorator(AbstractVehicle vehicle){
        super(vehicle);
    }
    public void decorateVehicle(){
        super.decorateVehicle();
        addWeapons();
    }
    public void addWeapons(){
        System.out.println("Added weapons to " + vehicle.getName());
    }
    @Override
    public int getExtraWeight(){
        return 1;
    }
    @Override
    public String getDecoratedName(){
        return "weapons";
    }
    // adding functionality
    public void fireExtraWeapons(){
        System.out.println("Firing extra weapons!");
    }
} // end of class

// ------------------------------------------------------------------

public class ArmourDecorator extends VehicleDecorator{
    public ArmourDecorator(AbstractVehicle vehicle){
        super(vehicle);
    }
    public void decorateVehicle(){
        super.decorateVehicle();
        addArmour();
    }
    public void addArmour(){
        System.out.println("Added armour to " + vehicle.getName());
    }
    @Override
    public int getExtraWeight(){
        return 3;
    }
    @Override
    public String getDecoratedName(){
        return "armour";
    }
} // end of class

// ------------------------------------------------------------------

public class VehicleDecoratorTest {
    public static void main(String args[]){

        System.out.println();
        AbstractVehicle vehicle1 = new WeaponsDecorator(new ArmourDecorator(
                                       new BattleTank("Sherman tank")));
        vehicle1.decorateVehicle();

        System.out.println();
        WeaponsDecorator vehicle2 = new WeaponsDecorator(new ArmourDecorator(
                                        new ChaseCar("Interceptor")));
        vehicle2.decorateVehicle();
        vehicle2.fireExtraWeapons();
    }
} // end of class

The Decorator Pattern in the Java APIs

The Decorator pattern is recognisable by creational methods taking an instance of the same interface/abstract type which adds additional behaviour. Some examples from the standard Java APIs:

  • Many of the subclasses of InputStreamOutputStreamReader and Writer in the java.io package have constructors taking an instance of the same type.
  • The collection wrapper methods checkedXXX()synchronizedXXX() and unmodifiableXXX() methods in the java.util.Collections class decorate other collections.
  • javax.servlet.http.HttpServletRequestWrapper and HttpServletResponseWrapper classes.

Comparison with Other Design Patterns

  • The Adapter pattern changes an object’s interface, while Decorator enhances an object’s responsibilities.
  • The Adapter pattern provides a different interface to its subject. The Proxy pattern provides the same interface. Decorator provides an enhanced interface.
  • Decorator and Proxy have similar structures, but different purposes.
  • Decorator lets us change the skin of an object; the Strategy pattern lets us change the internals.

What’s next?

In the weeks ahead, we’ll continue examining some of the more useful design patterns. Stay tuned! And don’t forget to share your comments.

Leave a Comment

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

Code like a Java Guru!

Thank You

We're Excited!

Thank you for completing the form. We're excited that you have chosen to contact us about training. We will process the information as soon as we can, and we will do our best to contact you within 1 working day. (Please note that our offices are closed over weekends and public holidays.)

Don't Worry

Our privacy policy ensures your data is safe: Incus Data does not sell or otherwise distribute email addresses. We will not divulge your personal information to anyone unless specifically authorised by you.

If you need any further information, please contact us on tel: (27) 12-666-2020 or email info@incusdata.com

How can we help you?

Let us contact you about your training requirements. Just fill in a few details, and we’ll get right back to you.

Your Java tip is on its way!

Check that incusdata.com is an approved sender, so that your Java tips don’t land up in the spam folder.

Our privacy policy means your data is safe. You can unsubscribe from these tips at any time.