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 CollectionarmourLayers; 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: BattleTank
, ChaseCar
and ArmouredCar
. Let’s say we limit the players to three optional upgrades: ExtraArmour
, ExtraWeapons
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:
Component
interface: This defines an interface to execute particular operations. In our game, this would be ourAbstractVehicle
.ConcreteComponent
: This implements the operations defined in theComponent
interface. These would be ourBattleTank
andChaseCar
classes.AbstractDecorator
: This is an abstract class that both extends theComponent
interface and contains aComponent
object. In the absence of this class, we need many subclasses ofConcreteDecorator
s for different combinations. Have the decorate contain aComponent
object (i.e. composition) reduces unnecessary subclasses. We’d write aVehicleDecorator
class for this.ConcreteDecorator
: These are the various implementations ofAbstractDecorator
. The sky’s the limit here –WeaponsDecorator
,ArmourDecorator
, 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
InputStream
,OutputStream
,Reader
andWriter
in thejava.io
package have constructors taking an instance of the same type. - The collection wrapper methods
checkedXXX()
,synchronizedXXX()
andunmodifiableXXX()
methods in thejava.util.Collections
class decorate other collections. javax.servlet.http.HttpServletRequestWrapper
andHttpServletResponseWrapper
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.