3 THE DECORATOR PATTERN
The intent of the Decorator pattern is to perform
additional actions on individual objects (Borella
2003; Gamma et al. 1995). The additional actions
and the decorated objects are selected at runtime. An
alternative for this pattern is inheritance. However,
the Decorator pattern has several advantages over
subclassing. First, additional actions can be added
and removed at runtime and per object. Second,
combining independent extensions is easy by
composing decorator objects. With subclassing
every combination of extensions results in a new
subclass and this can cause an explosion of
subclasses.
There are many variations of the Decorator
pattern, but in this paper the one used is that defined
by Borella (2003). The first AO approach to this
pattern was presented by Hannemann & Kiczales
(2002). Their solution has three limitations (Borella
2003). Firstly, it does not allow for dynamic
attaching and dynamic detaching of decorators.
Secondly, it is not possible at runtime to define an
order of activation among decorators. Thirdly, after
inserting the decorator aspect in the system, all
instances of the intended class are decorated.
Other solutions were proposed by Monteiro &
Fernandes (2004) and Hachani & Bardou (2002) but
these do not add anything new and so need not be
considered. A significant contribution to
implementing the Decorator pattern was made by
Borella (2003). He uses a per-target instantiation
model to decorate only a subset of the instances of a
class. His solution enables decorators to be
composed dynamically. The order of activation of
each decorator is given at runtime.
The Borella solution has two main
imperfections. Firstly, the around advice and the
wrap method are not generic, and depend on the type
of the decorated object. Secondly, the decorator
classes could directly implement the Decorator
interface. Introducing the Decorator interface via the
parent declaration unnecessarily complicates the
solution. Fig. 1 presents a new solution to the
Decorator pattern.
WrapperProtocol<E> describes the generic
structure and behaviour that is determined by the
pattern and it does not define any application
specific behaviour. This solution reduces the non-
reusable parts of the implementation. The around
advices are responsible for intercepting objects
which should be decorated and passing them to the
wrap(E e) method as argument. This method iterates
through all the registered decorators and gives them
its argument to decorate (Listing 1). After
decoration, the argument is returned. A concrete
decoration process is implemented in the
StarDecorator and DollarDecorator classes.
The joinpoints at which the object to decorate
should be captured are specified by the
returned_object and object_passed_as_argument
pointcuts. Thus it is possible to decorate the object,
which is passed as an argument or returned as a
result of a method call. The definitions of both
pointcuts are empty, so at least one of them should
be overridden in a subaspect.
The concrete subaspect, which knows what type
of object is captured and in which context, has to be
derived from WrapperProtocol<E> by giving a
bound type to the E parameter. One of such
subaspects is StringWrapper that binds the E
parameter with String. StringWrapper intercepts
requests to the getNextWord() method and performs
a decoration on the object returned by this method.
An example of the use of StringWrapper is shown in
Listing 2.
public E wrap(E e) {
for ( Decorator<E> dec: decorators.values() ) e=dec.decorate(e);
return e;
}
Listing 1: The wrap method.
public class testDecorator {
public static void main(String[] args) {
WordPrinter w = new WordPrinter(); //1
StringWrapper wrapper = StringWrapper.aspectOf(w); //2
wrapper.addDecorator( new StarDecorator(), 2 ); //3
wrapper.addDecorator( new DollarDecorator(), 1 ); //4
w.setWord(0,"XXX"); //5
System.out.println( w.getNextWord() ); //6
}
}
Listing 2: A use of the Decorator pattern.
ICSOFT 2010 - 5th International Conference on Software and Data Technologies
132