| This Chapter | |
| - | Chapter 21: Decorating Request Objects |
| - | The Decorator Pattern |
| - | A Trimmer Filter |
| - | Summary |
With inheritance, you can change the behavior of an object by extending the object’s class and override the methods whose behaviors you want to change. However, inheritance is powerless if the object in question is constructed by another subsystem in your application, such as an object factory or a servlet container.
For example, consider the Messenger class with a method named getMessage. You know the definition of the Messenger class and you can subclass it. However, in your application, a Messenger object always comes from a factory. This factory sets the initial state, including the String returned by getMessage, of every Messenger object it creates based on some unknown formulae. In other words, you cannot instantiate the Messenger class yourself.
In your application, the purpose of having a Messenger object is to pass it to the broadcast static method of a utility class called Util. Here is the implementation of the broadcast object.
public static void broadcast(Messenger messenger) {
System.out.print(messenger.getMessage());
}
In your own class, you have the following code.
Messenger messenger = aFactory.getMessenger(); Util.broadcast(messenger);
Suppose, you want to modify slightly the message printed by the broadcast method. You want it to be printed in capitals. What can you do? Technically, you could subclass Messenger, instantiate the subclass, and feed the returned object to Util.broadcast. However, that would be pointless because only the factory object knows how to initialize a Messenger object so that its getMessage method returns the correct value.
The Decorator pattern can help.
First you need to subclass Messenger. Let’s call the child class MessengerDecorator. Because MessengerDecorator is a subclass of Messenger, Util.broadcast will accept an instance of MessengerDecorator. However, MessengerDecorator is not just a child class. It is a decorator of a Messenger object. For this, there must be a constructor in MessengerDecorator that accepts the Messenger object to be decorated. Like this.
public MessengerDecorator(Messenger messenger)
This constructor simply assigns messenger to an internal variable. Now, you need to override the getMessage method in MessengerDecorator so that it prints the message in upper case. Because you have the reference to the original Messenger object, you can write your getMessage method like this:
public String getMessage() {
return this.messenger.getMessage().toUpperCase();
}
The getMessage method in MessengerDecorator returns the upper case version of the original message.
In your class, you can get a Messenger object as normal and pass the decorator to Util.broadcast.
Messenger messenger = factory.getMessenger(); Util.broadcast(new MessengerDecorator(messenger));
Instead of passing the original object to the intended destination, you pass to it a decorator of the object.
The example with the Messenger class above parallels the HttpServletRequest objects constructed by servlet containers. Upon receiving an HTTP request a servlet container creates an instance of HttpServletRequest and an instance of HttpServletResponse and pass the two objects to the specified servlet’s service method. Now if you can create a decorator for HttpServletRequest and pass it to the servlet’s service method without the servlet’s knowing the difference, you have implemented the Decorator pattern.
It is even easier with HttpServletRequest, because the Servlet API provides a wrapper class for HttpServletRequest: HttpServletRequestWrapper.
Let’s study how you can use decorate HttpServletRequest objects in the following example, a trimmer filter.