March 6th, 2011
8:53 pm
Using the Decorator pattern when extending the underlying Class

Posted under Java
Tags , , , , ,

In some cases, a Decorator just wraps an underlying class and does not add any interface changes of its own – it just changes behaviour without adding any new methods or properties. An example of this would be this ‘coffee’ example in the chapter on the Decorator pattern from the excellent Head First Design Patterns book by Eric and Elisabeth Freeman. In this case, it is not necessary to have visibility of individual decorators in the chain – they all add their required changes to the price and description of the coffee, and calling methods on the outermost decorator causes all the inner ones to be called to do their thing.

In other cases, however, it is necessary to add new methods and properties to a Decorator. An example from later in the same chapter would be the LineNumberInputStream decorator in the standard java.io package. this performs line numbering on the lines that pass through it, so it is obviously necessary to use the properties of that particular decorator in the chain to fetch the line numbers.

Therefore, in cases like this, client code will need to refer to particular decorators in the chain to access such methods and properties. This in no way invalidates the decorator pattern – it is important to note that we have still extended the underlying class using the open/closed principle, i.e. we have not modified it but decorated it with one or more new classes. Also and equally importantly, the various decorators in the chain are completely decoupled both from the underlying class and from each other as well.

There will be occasions where a decorator needs to extend the behaviour not just of the underlying base class, but also some of the behaviour of another decorator. In this case, such a decorator will extend the decorator it is adding behaviour to, rather than the underlying base class. The general guiding principles here are as follows:-

A decorator should extend the lowest level class it needs to in order to perform its function. For example, if A is a base class and B and C are decorators, C should not extend B unless it actually has dependencies on B’s additional behaviour, as otherwise we are introducing unnecessary coupling between objects that do not need to be coupled.

Note that all the decorators can and should be generic. Any subclass of the class a decorator extends may be passed to it, and may be retrieved generically. For example, if class ClassB decorates class ClassA, then we would declare B as follows:-

public class ClassB<C extends ClassA> extends ClassA {

private C decoratedClass;

}

This allows ClassB full access to all of the ClassA methods and properties, even when it is passed an arbitrary subclass of ClassA. Also, we have ensured that we can generically access via a ClassB instance, the particular subclass of ClassA used as the decorated class.

When we need to access particular decorators in a chain, we can either remember the object references used when we created them, or we can fetch references to them from their container decorator. In this case, we obviously have to keep knowledge of how the decorator chain was set up, and what was decorated in what order.

Decorators are a powerful pattern for flexible behaviour extension, but can have their downsides. You can end up with a large number of small decorator classes and large decorator chains – just look at the java.io package to see this. Also, you have to create all the delegated methods and properties for every decorator, although if you are using an IDE like Eclipse this can all be done automatically for you which is a big boon when using decorators heavily, and eliminates a lot of sources of error. Also, the major benefit compared with static extension of a class to add behaviour is that you can set up a decorator chain with any decorators you like in any order – illustrating again the benefits of favouring composition over inheritance.

No Comments »

Trackback URI | Comments RSS

Leave a Reply

You must be logged in to post a comment.