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:

1 2 3 Page
Recommended
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more