Further design patterns

The Decorator Pattern

The Decorator pattern provides the capability to transparently add functionality to a server object in a client/server system. Decorators can be nested thus providing the capability to add and remove a variety of additional functionalities, possibly dynamically.

The following diagram illustrates the Decorator pattern :-

    +------+                +-------------+
    |Client|                | Component   |
    |      |--------------->+-------------+
    +------+                | operation() |<-------------------------+
                            | ...         |                          |
                            +-------------+                          |
                                   ^                                 |
                                   |                                 |
             +---------------------+--------------------+            |
             |                                          |            |
     +-------+------------+                 +-----------+-----+      |
     | Concrete Component |                 | Decorator       |      |
     +--------------------+                 +-----------------+      |
     | operation()        |                 | operation() {   |      |
     | ...                |                 |   //extra code  |      |
     +--------------------+                 |   c.operation();|<>----+
                                            |   //extra code  |   c
                                            | }               |
                                            | ...             |
                                            +-----------------+  

The Component class is an abstract class defining the services provided by the server. This is sub-classed either to provide a Concrete Component object, or a Decorator, which itself contains a Component c within it.

The latter Component c is itself either a Decorator or the ConcreteComponent.

This provides the nesting capability which is perhaps shown better by the following diagram :-

             +----------------------------------------+
             |decorator 2                             |
             |              +-----------------------+ |
             |              |decorator 1            | |
client--->operation()       |           +---------+ | |
             |   |          |           |server   | | |
             | code2        |           |         | | |
             |   |          |           |         | | |
             |   +----->operation()     |         | | |
             |              |   |       |         | | |
             |              | code 1    |         | | |
             |              |   |       |         | | |
             |              |   +--->operation()  | | |
             |              |           |         | | |
             |              |           +---------+ | |
             |              |                       | |
             |              +-----------------------+ |
             |                                        |
             +----------------------------------------+

Here the server is contained as a component inside the decorator 1 which when called to perform operation(), executes the extra code code 1 before passing the call on to the server.

In a similar manner, the decorator 1 is contained inside decorator 2 which, before relaying on a request for operation() executes the addittional code code 2 and so on.

In this way, decorators can be wrapped around the server object like layers of an union. As all classes are sub-classes of Component this can be done in a way that is transparent to the client.

Java code example

The following example illustrates the process with a file server object accessed by a client via a resource manager which can decorate the file server with two different decorator objects.

Rather than providing the resource manager and client as two separate threads, for simplicity here, the client code is executed and creates a resource manager object to interact with.

To use the example, first compile the files in the following order :-

  1. File class.
  2. Component class.
  3. FileManager class.
  4. Logger class.
  5. UsageMonitor class.
  6. Resources class.
  7. Client class.

Then run the Client class. This will pop-up two windows, one providing the client interface and the other the resource manager one. The two windows may lie on top of oneanother and need to be moved.

When a decorator is selected within the resource manager window, another window will pop-up to display the output.

Decorators should be added before the server is passed on to the client, or if done later, the client should re-request the server.

Decorator pattern summary

The important features of the decorator pattern are :-


The Adaptor (or Wrapper) Pattern

The wrapper or adaptor pattern is similar in many ways to the decorator, but has some significant differences.

First of all its purpose is different, rather than providing a way of adding functionality to a server, it is used to make one server appear like another.

This is very useful if there is a large amount of client code in existence and it is necessary to replace a server object which they use with a new one which performs essentially the same function but provides a different interface.

In this situation, the wrapper is used to make the new server look like (offer the same interface) as the old one.

In essence the difference between the two patterns can be sumarised as follows :-

The class diagram for a wrapper looks something like the following :-

      +------+             +-------------+        +-------------+
      |Client|             | Target      |        | Adaptee     |
      |      |------------>+-------------+        +-------------+
      +------+             | Request()   |        | ActualReq() | 
                           +-------------+        +-------------+
                                  ^                      ^
                                  | public       private |
                                  +----------------------+
                                              |
                                       +---------------+
                                       | Adapter       |
                                       +---------------+
                                       | Request(){    |
                                       |  ActualReq(); |
                                       | }             |
                                       +---------------+

This wrapper is called a class wrapper and relies on multiple. The Target server object is subclassed to provide the standard interface to the clients, the new server (or Adaptee) is also a super-class of the Adapter. This consequently inherits the new server interface as well.

Calls made by the client to the old (Target) interface are then handled by making calls to the newly inherited Adaptee methods.

Making the inheritance of the Adaptee private prevents the client from seeing the new server interface.

As an alternative to the class adapter, in situations where multiple inheritance is not available, the following object adapter pattern can be used :-

      +------+             +-------------+        +-------------+
      |Client|             | Target      |        | Adaptee     |
      |      |------------>+-------------+        +-------------+
      +------+             | Request()   |        | ActualReq() | 
                           +-------------+        +-------------+
                                  ^                     |
                                  |                     |
                          +-----------------+           |
                          | Adapter         |           |
                          +-----------------+           |
                          | Request(){      |<>---------+
                          |  c.ActualReq(); | c
                          | }               |
                          +-----------------+

Here the Adapter contains the Adaptee as a component, c. Note that only one level of nesting is appropriate.

Mixing patterns

Frequently we desire to modify patterns for a particular purpose or to combine together several patterns. This is legitimate as patterns are only intended to be basic building blocks which can be adapted to suit specific needs.

For instance, we may have a new server class which essentially provides the functionality of an existing one and wish to monitor how the new one performs.

What would be appropriate here would be a wrapper object which also acts as a decorator - thus borrowing from both patterns to form a hybrid one.


Press to return to course home page.