Decorators

Specialization allows us to add features to a class, but in some situations we need to add different combinations of features to individual objects. For example, a grid encapsulates and manages a dynamic two dimensional array of characters that provides "poor man's" graphics:

http://www.cs.sjsu.edu/faculty/pearce/java2/jpop/chp6/image567.gif

Suppose we will also need a shaded grid (i.e., a grid with a shaded background), a bordered grid (i.e., a grid with a border around it), a read-only grid (i.e., a grid with disabled plot functions), a shaded-read-only grid, a bordered-read-only grid, a shaded-bordered grid, and a shaded-bordered-read-only grid. If we create a class for each combination of these features we will end up with a complicated hierarchy of seven classes:

http://www.cs.sjsu.edu/faculty/pearce/java2/jpop/chp6/image568.gif

Adding a new feature, such as resizable grid, will add six new derived classes. In general, if we are contemplating n features, there will be n! (= n * (n - 1) * ... * 1) combinations of these features. This can lead to a combinatorial explosion in the number of classes we must implement and maintain.

We can avoid this maintenance nightmare by using delegation instead of specialization to add features. For each feature we simply create a handle object called a decorator that implements the feature, then delegates to its body. The body may be the original component or another decorator. Thus, decorators can be chained together to dynamically provide any combination of features:

http://www.cs.sjsu.edu/faculty/pearce/java2/jpop/chp6/image569.gif

The Decorator Pattern formalizes this idea:

Decorator [Go4]

Other Names

Wrapper

Problem

In some situations we may want to add features to an individual object rather than an entire class.

Solution

For each additional feature create a new "decorator" class that implements the feature. The decorator class also implements the interface of the original object by delegating to the original object or perhaps to an instance of another decorator class. In this way decorators (i.e. instances of decorator classes) can be chained together.

Static Structure

Assume we want to add features to an instance of the Component class that implements a component interface. Each decorator class must also implement the component interface. In this way the client never knows if it is dealing with a component or a decorator. Each decorator must be able to delegate to its body, which may be the original component or another decorator. (Thus, decorators are component interface clients, too!) For convenience, we place the delegation machinery in a decorator base class:

http://www.cs.sjsu.edu/faculty/pearce/java2/jpop/chp6/image708.gif

For example, the services are virtual functions in the component interface:

interface IComponent {
   void serviceA();
   void serviceB();
   // etc.
}

The basic services are implemented in the Component class:

class Component implements IComponent {
   void serviceA() { ... }
   void serviceB() { ... }
   // etc.
}

The Decorator base class simply delegates to its body:

class Decorator implements IComponent {
   Decorator(IComponent c) { myBody = c; }
   void serviceA() { myBody.serviceA(); }
   void serviceB() { myBody.serviceB(); }
   // etc.
   private IComponent myBody;
}

Suppose the concrete decorator class Decorator1 simply enhances serviceA() with some added behavior, then it only needs to re-implement serviceA() (the Decorator class provides default implementations for the other services). The last line performs the delegation:

class Decorator1 extends Decorator {
   Decorator1(IComponent c) { super(c); }
   void serviceA() {
      // added behavior goes here
      super.serviceA(); // delegates to body
   }
}

Here is how a client might create a reference, z, to an object that implements the basic component interface, but with the features added by Decorator1 and Decorator2:

IComponent x = new Component();
IComponent y = new Decorator1(x);
IComponent z = new Decorator2(y);

Of course the original component and the Decorator1 instance can be anonymous, so we can create z in a single line:

IComponent z = new Decorator2(new Decorator1(new Component()));

Here is a sequence diagram showing the chain of delegations that occur when the client calls z.serviceA():

http://www.cs.sjsu.edu/faculty/pearce/java2/jpop/chp6/image571.gif

Be careful, as with the Envelope-Letter idiom, a decorator derives from the component interface and delegates to an instance of the component interface. We need decorators to be derived from the component interface so they can be treated like components by clients and other decorators. However, a decorator must be careful never to use the functions and attributes it inherits from the component interface base class. Instead, only the functions and attributes of the body must be used. For example, assume state is a component interface attribute.

abstract class IComponent {
   protected State state;
   // etc.
}

The true state of z is x.state, not the inherited z.state.

Streams

The decorator pattern is used to add features to java streams. Recall the makeWriter() method of the Tools class:

static public PrintWriter makeWriter(OutputStream os) {
   return new PrintWriter(
      new BufferedWriter(
         new OutputStreamWriter(os)), true);
}

This method constructs an output stream writer, places a buffered writer decorator in front of it, then places a print writer decorator in front of that. Each extends the abstract Writer interface/class:

http://www.cs.sjsu.edu/faculty/pearce/java2/jpop/chp6/image709.gif

A call to this method constructs the decorator chain:

http://www.cs.sjsu.edu/faculty/pearce/java2/jpop/chp6/image710.gif