Take control with the Proxy design pattern

The Proxy design pattern substitutes a proxy for an object, making your apps more efficient

A friend of mine -- a medical doctor, no less -- once told me that he convinced a friend to take a college exam for him. Someone who takes the place of someone else is known as a proxy. Unfortunately for my friend, his proxy drank a bit too much the night before and failed the test.

In software, the Proxy design pattern proves useful in numerous contexts. For example, using the Java XML Pack, you use proxies to access Web services with JAX-RPC (Java API for XML-based remote procedure calls). Example 1 shows how a client accesses a simple Hello World Web service:

Example 1. A SOAP (Simple Object Access Protocol) proxy

public class HelloClient {
    public static void main(String[] args) {
        try {
            HelloIF_Stub proxy = (HelloIF_Stub)(new HelloWorldImpl().getHelloIF());
            proxy._setTargetEndpoint(args[0]);
            System.out.println(proxy.sayHello("Duke!"));
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

Example 1's code closely resembles the Hello World Web services example included with JAX-RPC. The client obtains a reference to the proxy, and sets the proxy's endpoint (the Web service's URL) with a command line argument. Once the client has a reference to the proxy, it invokes the proxy's sayHello() method. The proxy forwards that method call to the Web service, which often resides on a different machine than that of the client.

Example 1 illustrates one use for the Proxy design pattern: accessing remote objects. Proxies also prove useful for creating expensive resources on demand, a virtual proxy, and for controlling access to objects, a protection proxy.

If you've read my "Decorate Your Java Code" (JavaWorld, December 2001), you may see similarities between the Decorator and Proxy design patterns. Both patterns use a proxy that forwards method calls to another object, known as the real subject. The difference is that, with the Proxy pattern, the relationship between a proxy and the real subject is typically set at compile time, whereas decorators can be recursively constructed at runtime. But I'm getting ahead of myself.

In this article, I first introduce the Proxy pattern, starting with a proxy example for Swing icons. I conclude with a look at the JDK's built-in support for the Proxy pattern.

Note: In the first two installments of this column -- "Amaze Your Developer Friends with Design Patterns" (October 2001) and "Decorate Your Java Code" -- I discussed the Decorator pattern, which closely relates to the Proxy pattern, so you may wish to look at these articles before proceeding.

The Proxy pattern

Proxy: Control access to an object with a proxy (also known as a surrogate or placeholder).

Swing icons, for reasons discussed in the "Proxy Applicability" section below, represent an excellent choice for illustrating the Proxy pattern. I begin with a short introduction to Swing icons, followed by a discussion of a Swing icon proxy.

Swing icons

Swing icons are small pictures used in buttons, menus, and toolbars. You can also use Swing icons by themselves, as Figure 1 illustrates.

Figure 1. A Swing icon

The application shown in Figure 1 is listed in Example 2:

Example 2. Swing icons

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
// This class tests an image icon.
public class IconTest extends JFrame {
   private static String IMAGE_NAME = "mandrill.jpg";
   private static int FRAME_X = 150,      FRAME_Y = 200, 
                  FRAME_WIDTH = 268, FRAME_HEIGHT = 286;
   private Icon imageIcon = null, imageIconProxy = null;
   static public void main(String args[]) {
      IconTest app = new IconTest();
      app.show();
   }
   public IconTest() {
      super("Icon Test");
      imageIcon = new ImageIcon(IMAGE_NAME);
      setBounds(FRAME_X, FRAME_Y, FRAME_WIDTH, FRAME_HEIGHT);
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   }
   public void paint(Graphics g) {
      super.paint(g);
      Insets insets = getInsets();
      imageIcon.paintIcon(this, g, insets.left, insets.top);
   }
}

The preceding application creates an image icon -- an instance of javax.swing.ImageIcon -- and then overrides the paint() method to paint the icon.

Swing image-icon proxies

The application shown in Figure 1 is a poor use of Swing image icons because you should use image icons only for small pictures. That restriction exists because creating images is expensive, and ImageIcon instances create their images when they are constructed. If an application creates many large images at once, it could cause a significant performance hit. Also, if the application does not use all of its images, it's wasteful to create them upfront.

A better solution loads images as they become needed. To do so, a proxy can create the real icon the first time the proxy's paintIcon() method is called. Figure 2 shows an application that contains an image icon (on the left) and an image-icon proxy (on the right). The top picture shows the application just after its launch. Because image icons load their images when they are constructed, an icon's image displays as soon as the application's window opens. In contrast, the proxy does not load its image until it is painted for the first time. Until the image loads, the proxy draws a border around its perimeter and displays "Loading image..." The bottom picture in Figure 2 shows the application after the proxy has loaded its image.

Figure 2. An icon (left) and an icon proxy (right)

I've listed the application shown in Figure 2 in Example 3:

Example 3. Swing icon proxies

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
// This class tests a virtual proxy, which is a proxy that
// delays loading an expensive resource (an icon) until that 
// resource is needed.
public class VirtualProxyTest extends JFrame {
   private static String IMAGE_NAME = "mandrill.jpg";
   private static int IMAGE_WIDTH = 256, IMAGE_HEIGHT = 256,
                          SPACING = 5,        FRAME_X = 150,
                          FRAME_Y = 200, FRAME_WIDTH = 530, 
                     FRAME_HEIGHT = 286;
   private Icon imageIcon = null, imageIconProxy = null;
   static public void main(String args[]) {
      VirtualProxyTest app = new VirtualProxyTest();
      app.show();
   }
   public VirtualProxyTest() {
      super("Virtual Proxy Test");
      // Create an image icon and an image-icon proxy.
      imageIcon = new ImageIcon(IMAGE_NAME);
      imageIconProxy = new ImageIconProxy(IMAGE_NAME,
                                          IMAGE_WIDTH, 
                                          IMAGE_HEIGHT);
      // Set the bounds of the frame, and the frame's default
      // close operation.
      setBounds(FRAME_X, FRAME_Y, FRAME_WIDTH, FRAME_HEIGHT);
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   }
   public void paint(Graphics g) {
      super.paint(g);
      Insets insets = getInsets();
      imageIcon.paintIcon(this, g, insets.left, insets.top);
      imageIconProxy.paintIcon(this, g, 
               insets.left + IMAGE_WIDTH + SPACING, // width
               insets.top); // height
   }
}

Example 3 is nearly identical to Example 2, except for the addition of the image-icon proxy. The Example 3 application creates the icon and the proxy in its constructor, and overrides its paint() method to paint them. Before discussing the proxy's implementation, look at Figure 3, which is a class diagram of the proxy's real subject, the javax.swing.ImageIcon class.

Figure 3. ImageIcon's class diagram. Click on thumbnail to view full-size image.

The javax.swing.Icon interface, which defines the essence of Swing icons, includes three methods: paintIcon(), getIconWidth(), and getIconHeight(). The ImageIcon class implements the Icon interface, and adds methods of its own. Image icons also maintain a description of, and a reference to, their images.

Image-icon proxies implement the Icon interface and maintain a reference to an image icon -- the real subject -- as the class diagram in Figure 4 illustrates.

Figure 4. ImageIconProxy class diagram. Click on thumbnail to view full-size image.

The ImageIconProxy class is listed in Example 4.

Example 4. ImageIconProxy.java

// ImageIconProxy is a proxy (or surrogate) for an icon.
// The proxy delays loading the image until the first time the
// image is drawn. While the icon is loading its image, the
// proxy draws a border and the message "Loading image..."
class ImageIconProxy implements javax.swing.Icon {
   private Icon realIcon = null;
   boolean isIconCreated = false;
   private String  imageName;
   private int     width, height;
   public ImageIconProxy(String imageName, int width, int height){
      this.imageName = imageName;
      this.width = width;
      this.height = height;
   }
   public int getIconHeight() {
      return isIconCreated ? height : realIcon.getIconHeight(); 
   }
   public int getIconWidth() {
      return isIconCreated realIcon == null ? width : realIcon.getIconWidth(); 
   }
   // The proxy's paint() method is overloaded to draw a border
   // and a message ("Loading image...") while the image 
   // loads. After the image has loaded, it is drawn. Notice
   // that the proxy does not load the image until it is
   // actually needed.
   public void paintIcon(final Component c, 
                               Graphics g, int x, int y) {
      if(isIconCreated) {
         realIcon.paintIcon(c, g, x, y);
      }
      else {
         g.drawRect(x, y, width-1, height-1);
         g.drawString("Loading image...", x+20, y+20);
         // The icon is created (meaning the image is loaded)
         // on another thread. 
         synchronized(this) {
            SwingUtilities.invokeLater(new Runnable() {
                 public void run() {
                  try {
                     // Slow down the image-loading process.
                     Thread.currentThread().sleep(2000);
                     // ImageIcon constructor creates the image.
                     realIcon = new ImageIcon(imageName);
                     isIconCreated = true;
                  }
                  catch(InterruptedException ex) {
                       ex.printStackTrace();
                  }
                  // Repaint the icon's component after the
                  // icon has been created.
                  c.repaint();
               }
            });
         }
      }
   }
}

ImageIconProxy maintains a reference to the real icon with the realIcon member variable. The first time the proxy is painted, the real icon is created on a separate thread to allow the rectangle and string to be painted (the calls to g.drawRect() and g.drawString() do not take effect until the paintIcon() method returns). After the real icon is created, and therefore the image is loaded, the component that displays the icon is repainted. Figure 5 shows a sequence diagram for those events.

Figure 5. Paint an image proxy. Click on thumbnail to view full-size image.

The Figure 5's sequence diagram is typical of all proxies: Proxies control access to their real subject. Because of that control, proxies often instantiate their real subject, as is the case for the image icon proxy listed in Example 4. That instantiation is one of the differences between the Proxy pattern and the Decorator pattern: Decorators rarely create their real subjects.

The JDK's built-in support for the Proxy design pattern

The Proxy pattern is one of the most important design patterns because it provides an alternative to extending functionality with inheritance. That alternative is object composition, where an object (proxy) forwards method calls to an enclosed object (real subject).

Object composition is preferable to inheritance because, with composition, enclosing objects can only manipulate their enclosed object through the enclosed object's interface, which results in loose coupling between objects. In contrast, with inheritance, classes are tightly coupled to their base class because the internals of a base class are visible to its extensions. Because of that visibility, inheritance is often referred to as white-box reuse. On the other hand, with composition, the internals of the enclosing object are not visible to the enclosed object (and vice-versa); therefore, composition is often referred to as black-box reuse. All things being equal, black-box reuse (composition) is preferable to white-box reuse (inheritance) because loose coupling results in more malleable and flexible systems.

Because the Proxy pattern is so important, J2SE 1.3 (Java 2 Platform, Standard Edition) and beyond directly supports it. That support involves three classes from the java.lang.reflect package: Proxy, Method, and InvocationHandler. Example 5 shows a simple example that utilizes the JDK support for the Proxy pattern:

Example 5. JDK proxies

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface AnInterface {
   public void doSomething();
}
class AClass implements AnInterface {
   public void doSomething() {
      System.out.println("Inside Method AClass.doSomething()");
   }
}
public class Test {
   public static void main(String args[]) {
      AnInterface realSubject = new AClass();
      AnInterface proxy = (AnInterface)Proxy.newProxyInstance(
                        realSubject.getClass().getClassLoader(),
                        realSubject.getClass().getInterfaces(),
                        new SimpleInvocationHandler(realSubject));
      passMeAProxy(proxy);
   }
   private static void passMeAProxy(AnInterface anInterface) {
      anInterface.doSomething();
   }
}
class SimpleInvocationHandler implements InvocationHandler {
   public SimpleInvocationHandler(Object realSubject) {
      this.realSubject = realSubject;
   }
   public Object invoke(Object proxy, Method m, Object[] args){
      Object result = null;
      System.out.println("Before Calling " + m.getName());
      try {
         result = m.invoke(realSubject, args);   
      }
      catch(Exception ex) {
         System.exit(1);
      }
      System.out.println("After Calling " + m.getName());
      return result;
   }
   private Object realSubject = null;
}

In the preceding example, the static Proxy.newProxyInstance() method creates a proxy for a real subject. Real subjects must implement one or more interfaces, and a reference to a proxy can be passed to any method that expects a reference to one of those interfaces. The main() method passes the proxy to a method that takes a reference to AnInterface, just to prove it can be done. In the preceding example, our simple proxy implements only one interface.

Proxy.newProxyInstance() takes three arguments: the class loader that loaded the real subject, a list of interfaces implemented by the real subject, and a reference to an invocation handler.

Every time you invoke a proxy's method, the proxy calls its invocation handler's invoke() method. The proxy passes itself to the invoke() method, along with a reference to the proxy's method and its list of arguments. In the preceding code, SimpleInvocationHandler.invoke() invokes the specified method on the real subject. Here's the output of the application listed in Example 5:

Before Calling doSomething
Inside Method AClass.doSomething()
After Calling doSomething

Image-icon proxy, revisited

Example 6 shows how you can use the JDK's built-in support for the Proxy pattern to reimplement the image-icon proxy listed in Example 3:

Example 6. Image-icon proxies, revisited

import java.lang.reflect.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
// This class tests a virtual proxy, which 
// delays loading an expensive resource (an icon) until that 
// resource is needed.
public class VirtualProxyTest extends JFrame {
   private static String IMAGE_NAME = "mandrill.jpg";
   private static final int IMAGE_WIDTH = 256, IMAGE_HEIGHT = 256,
                                SPACING = 5,        FRAME_X = 150,
                                FRAME_Y = 200, FRAME_WIDTH = 530, 
                           FRAME_HEIGHT = 286;
   private ImageIcon imageIcon = null;
   private Icon imageIconProxy = null;
   static public void main(String args[]) {
      VirtualProxyTest app = new VirtualProxyTest();
      app.show();
   }
   public VirtualProxyTest() {
      super("Virtual Proxy Test");
      // Create an image icon and an image-icon proxy.
      imageIcon = new ImageIcon(IMAGE_NAME);
      imageIconProxy = (Icon)Proxy.newProxyInstance(
                        imageIcon.getClass().getClassLoader(),
                        imageIcon.getClass().getInterfaces(),
                        new ImageIconInvocationHandler(IMAGE_NAME,
                                    IMAGE_WIDTH, IMAGE_HEIGHT));
      // Set the bounds of the frame, and the frame's default
      // close operation.
      setBounds(FRAME_X, FRAME_Y, FRAME_WIDTH, FRAME_HEIGHT);
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   }
   public void paint(Graphics g) {
      super.paint(g);
      Insets insets = getInsets();
      imageIcon.paintIcon(this, g, insets.left, insets.top);
      imageIconProxy.paintIcon(this, g, 
               insets.left + IMAGE_WIDTH + SPACING, // width
               insets.top); // height
   }
}
// ImageIconInvocationHandler is a proxy for an icon.
// The proxy delays loading the image until the first time 
// the image is drawn. While the icon is loading its image, the
// proxy draws a border and the message "Loading image..."  
class ImageIconInvocationHandler implements InvocationHandler {
   private static final String PAINT_ICON_METHOD="paintIcon",
                               GET_WIDTH_METHOD ="getIconWidth",
                               GET_HEIGHT_METHOD="getIconHeight";
   private ImageIcon realIcon = null;
   private boolean isIconCreated = false;
   private String   imageName = null;
   private int width, height;
   public ImageIconInvocationHandler(String imageName,
                                      int width, int height) {
      this.width = width;
      this.height = height;
      this.imageName = imageName;
   }
   public Object invoke(Object proxy, Method m, Object[] args) {
      Object result = null;
      if(PAINT_ICON_METHOD.equals(m.getName())) {
         final Component c = (Component)args[0];
         final Graphics g = (Graphics)args[1];
         final int x = ((Integer)args[2]).intValue(),
                   y = ((Integer)args[3]).intValue();
         if(isIconCreated) {
              realIcon.paintIcon(c, g, x, y);
         }
         else {
              g.drawRect(x, y, width-1, height-1);
            g.drawString("Loading image...", x+20, y+20);
            // The image is loaded via a Runnable passed to
            // SwingUtilties.invokeLater(), which means that
            // it is executed on another thread. 
            SwingUtilities.invokeLater(new Runnable() {
               public void run() {
                  try {
                     // Slow down the image-loading process.
                     Thread.currentThread().sleep(2000);
                     // ImageIcon constructor creates the image.
                     realIcon = new ImageIcon(imageName);
                     isIconCreated = true;
                  }
                  catch(InterruptedException ex) {
                     ex.printStackTrace();
                  }
                  c.repaint();
               }
            });
         }
      }
      else if(GET_WIDTH_METHOD.equals(m.getName())) {
         return isIconCreated ?  new Integer(height) : 
                new Integer(realIcon.getIconHeight()); 
      }
      else if(GET_HEIGHT_METHOD.equals(m.getName())) {
         return isIconCreated ?  new Integer(width) : 
                new Integer(realIcon.getIconWidth())rticle.txt
                ; 
      }
      return null;
   }
}

If you compare Example 6 with Example 3, you may conclude that the JDK's built-in support for the Proxy pattern is not for you, because Example 6 is a bit more complicated than Example 3. In this case, I would prefer Example 3 myself -- the JDK's built-in support for the Proxy pattern is much more useful when a proxy implements many methods that simply delegate to the real subject with only a few methods that embellish the real subject.

Proxy applicability

The Proxy pattern applies whenever you need to control access to an object. The most common situations include:

  • Remote proxies
  • Virtual proxies
  • Protection proxies

Remote proxies control access to remote objects, such as the Web service proxy discussed at the beginning of this article. Virtual proxies -- the image-icon proxy is an example -- control access to resources that are expensive to create, such as large images. Protection proxies control what functionality specific users can access.

Classes that implement stable interfaces with few methods are the best candidates for a proxy's real subject because their proxies are easy to implement and maintain. For example, the Icon interface defines just three methods, unchanged since Swing's 1998 release. Writing an image-icon proxy is a fairly simple matter, but, for example, creating a proxy whose real subject is a Swing component proves much harder because you literally must implement hundreds of forwarding methods. Of course, the JDK's built-in Proxy pattern support makes it much easier to implement proxies whose real subjects have a large number of methods.

Finally, let's look quickly at the difference between the Decorator and Proxy patterns. Although you implement both patterns in an almost identical fashion, it's the intent of those patterns that differs. The Decorator pattern constructs objects at runtime by recursively enclosing an object within one or more decorators. The Proxy pattern acts as a stand-in for a real subject, which is set at compile time.

Homework

For this month's homework assignment, reimplement the TableBubbleSortDecorator discussed in "Decorate Your Java Code" with the JDK's built-in Proxy pattern support.

Homework from last time

Last month's homework asked you to implement a filter decorator -- extending the TableFilterDecorator class shown in Figure 8 from "Decorate Your Java Code" -- that filters high-priced items in the table shown in that article's Figure 6.

The filter I implemented for this assignment filters items that cost more than one dollar. Figure 1H shows the application discussed last time fitted with a button that applies the filter to the application's table. Figure 2H shows the application after the filter button has activated.

Figure 1H. Before filtering
Figure 2H. After filtering

The application shown in Figure 1H and Figure 2H is partially listed in Example 1H:

Example 1H. A filter decorator

...
public class Test extends JFrame {
   public static void main(String args[]) {
      SwingApp.launch(new Test(), "A Sort Decorator", 
                                   300, 300, 450, 250);
   }
   public Test() {
      // Create the decorator that will decorate the table's 
      // original model. The reference must be final because it's
      // accessed by an inner class below. Notice that the 
      // reference is TableSortDecorator even though it
      // points to a TableBubbleSortDecorator--that
      // way, the mouse listener below will work with any type
      // of TableSortDecorator.
      final TableSortDecorator sortDecorator = 
            new TableBubbleSortDecorator(table.getModel());
      final TableFilterDecorator filterDecorator =
            new TableHighPriceFilter(sortDecorator);
      // Set the table's model to the decorator. Because the
      // decorator implements TableModel, the table will never
      // know the difference between the decorator and its
      // original model.
      table.setModel(filterDecorator);
      ...
      filterButton.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            filterDecorator.filter(PRICE_COLUMN);
            repaint();
         }
      });
   }
   ...
}

The application shown above wraps the table's original model with a sort decorator, which is subsequently wrapped with a filter decorator. For this homework assignment, our focus is the filter decorator.

Figure 3H shows the class diagram for the filter, which decorates a table model:

Figure 3H. Filter decorator class diagram. Click on thumbnail to view full-size image.

TableHighPriceFilter, by way of TableModelDecorator, implements the TableModel interface and forwards method calls to an enclosed table model instance -- the realModel. TableHighPriceFilter implements TableFilterDecorator, which is listed in Example 3H.

Note: The TableModelDecorator class is listed in "Decorate Your Java Code."

Example 3H. The abstract TableFilterDecorator class

import javax.swing.table.TableModel;
public abstract class TableFilterDecorator extends 
                                         TableModelDecorator {
   // Extensions of TableFilterDecorator must implement the
   // abstract filter method, in addition to tableChanged. The
   // latter is required because TableModelDecorator
   // implements the TableModelListener interface.
   abstract public void filter(int column);
   public TableFilterDecorator(TableModel realModel) {
      super(realModel);
   }
}

Example 4H lists the TableHighPriceFilter class:

Example 4H. The abstract TableFilterDecorator class

import javax.swing.table.TableModel;
import javax.swing.event.TableModelEvent;
public class TableHighPriceFilter extends TableFilterDecorator {
   // The lone constructor must be passed a reference to a
   // TableModel. This class decorates that model with additional
   // sorting functionality.
   public TableHighPriceFilter(TableModel model) {
      super(model);
      allocate();
   }
   // tableChanged is defined in TableModelListener, which
   // is implemented by TableFilterDecorator.
   public void tableChanged(TableModelEvent e) {
      allocate();   
   }
   // Three TableModel methods are overridden from
   // TableModelDecorator.
   public Object getValueAt(int row, int column) {
      return getRealModel().getValueAt(indexes[row], column);
   }
   public void setValueAt(Object aValue, int row, int column) {
      getRealModel().setValueAt(aValue, row, column);
   }
   public int getRowCount() {
      return rowCount == NOT_INITIALIZED ? 
                       getRealModel().getRowCount() : rowCount;
   }
   // The filter method filters out items that cost more than
   // one dollar.
   public void filter(int column) {
      int rowsNow = getRowCount();
      int[] newIndexes = new int[rowsNow];
      int newRowCount = rowsNow;
      for(int i=0, j=0; i < rowsNow && j < rowsNow;) {
         String price = (String)getRealModel().
                                getValueAt(indexes[j], column);
         if(price.startsWith("$."))    { // less than a dollar
            newIndexes[i++] = indexes[j++];
         }
         else {
            newRowCount--;
            ++j;
            continue;
         }
      }
      rowCount = newRowCount;
      for(int i=0; i < rowCount; ++i)
         indexes[i] = newIndexes[i];
   }
   private void allocate() {
      indexes = new int[getRowCount()];
      for(int i=0; i < indexes.length; ++i) {
         indexes[i] = i;         
      }
   }
   private int indexes[];
   private static int NOT_INITIALIZED = -1;
   private int rowCount = NOT_INITIALIZED;
}

The TableHighPriceFilter class inherits forwarding methods -- which forward to the actual model -- from TableModelDecorator. For some decorators, forwarding methods can constitute a good deal of code, so it's convenient to encapsulate those methods in a base class for other decorators to extend.

Email

Thanks for all the email you've sent in response to the first two Java Design Patterns installments. I've had some interesting discussions about design patterns and object-oriented (OO) software development. Here's one such exchange that points out the differences between object composition and delegation, and why the Decorator pattern uses the former but not the latter:

Tom Palmer wrote:

Decorators are a form of dynamic inheritance system, but with normal OO inheritance, no matter where a method is implemented in a hierarchy, this always refers to the same object. With normal decorators, however, once a call is passed on, the wrapped object doesn't know about the original object. It's as if the super method call forgot who this was.

There's an almost convenient mechanism for passing the this reference: two versions of each method, one with an extra parameter representing the effective this, which I call the target. The short form of each method just calls the expanded form with the current this as the target. Automatic design patterns via proxies can be made to look for expanded methods and pass in the proper target object.

Palmer makes a good point. Instead of comparing decorators to inheritance, it's more appropriate to compare the more general composition or delegation techniques (which he describes above) to inheritance.

Decorators use object composition by forwarding method calls to an enclosed object. It's called composition because the enclosed object cannot access its enclosing object (the this reference he discusses above).

Delegation is just like composition, except the enclosing object passes a reference to itself to the enclosed instance. There are several ways for the enclosing object to make itself known to the enclosed object -- what Palmer describes above is one way.

For the Decorator pattern, you use composition instead of delegation, because delegation requires the enclosed object to be aware of its enclosing object. One of the Decorator pattern's main attractions is that enclosed objects (and the objects that use the decorators) are oblivious to decorators. For example, in one example discussed in "Decorate Your Java Code," table models remain unaware of the sorters that decorate them, so that any table model can be sorted.

David Geary is the author of Advanced JavaServer Pages (Prentice Hall, 2001; ISBN: 0130307041) and the Graphic Java series (Sun Microsystems Press). David has been developing object-oriented software in numerous object-oriented languages for 17 years. Since the Gang of Four's Design Patterns book was published in 1994, David has been an active proponent of design patterns, and has used and implemented design patterns in Smalltalk, C++, and Java. In 1997, David began working full-time as an author and occasional speaker and consultant. David is a member of the expert groups defining the JSP (JavaServer Pages) standard custom tag library and JavaServer Faces, and is a contributor to the Apache Struts JSP framework.

Learn more about this topic

Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more