C# Design Patterns: The Proxy Pattern
The Proxy pattern is used when you need to represent an object that is complex or time consuming to create with a simpler one. If creating an object is expensive in time or computer resources, Proxy allows you to postpone this creation until you need the actual object. A Proxy usually has the same methods as the object it represents, and once the object is loaded, it passes on the method calls from the Proxy to the actual object.
There are several cases where a Proxy can be useful.
-
An object, such as a large image, takes a long time to load.
-
The results of a computation take a long time to complete, and you need to display intermediate results while the computation continues.
-
The object is on a remote machine, and loading it over the network may be slow, especially during peak network load periods.
-
The object has limited access rights, and the proxy can validate the access permissions for that user.
Proxies can also be used to distinguish between requesting an instance of an object and the actual need to access it. For example, program initialization may set up a number of objects that may not all be used right away. In that case, the proxy can load the real object only when it is needed.
Let’s consider the case of a large image that a program must load and display. When the program starts, there must be some indication that an image is to be displayed so that the screen lays out correctly, but the actual image display can be postponed until the image is completely loaded. This is particularly important in programs such as word processors and Web browsers that lay out text around the images even before the images are available.
An image proxy can note the image and begin loading it in the background while drawing a simple rectangle or other symbol to represent the image’s extent on the screen before it appears. The proxy can even delay loading the image at all until it receives a paint request and only then begin the process.
Sample Code
In this example, we create a simple program to display an image on an Image control when it is loaded. Rather than loading the image directly, we use a class we call ImageProxy to defer loading and draw a rectangle until loading is completed.
private void init() { imgProxy = new ImageProxy (); } //----- public Form1() { InitializeComponent(); init(); } //----- private void button1_Click(object sender, EventArgs e) { Pic.Image = imgProxy.getImage (); }
Note that we create the instance of the ImageProxy just as we would have for an Image. The ImageProxy class sets up the image loading and creates an Imager object to follow the loading process. It returns a class that implements the Imager interface.
public interface Imager { Image getImage() ; }
In this simple case, the ImageProxy class just delays five seconds and then switches from the preliminary image to the final image. It does this using an instance of the Timer class. Timers are handled using a TimerCallback class that defines the method to be called when the timer ticks. This is very similar to the way we add other event handlers. And this callback method, timerCall, sets the done flag and turns off the timer.
public class ImageProxy { private bool done; private Timer timer; //----- public ImageProxy() { //create a timer thread and start it timer = new Timer ( new TimerCallback (timerCall), this, 5000, 0); } //----- //called when timer completes private void timerCall(object obj) { done = true; timer.Dispose (); } //----- public Image getImage() { Imager img; if (done) img = new FinalImage (); else img = new QuickImage (); return img.getImage (); } }
We implement the Imager interface in two tiny classes we called QuickImage and FinalImage. One gets a small gif image and the other a larger (and presumably slower) jpeg image. In C#, Image is an abstract class, and the Bitmap, Cursor, Icon, and Metafile classes are derived from it. So the actual class we will return is derived from Image. The QuickImage class returns a Bitmap from a gif file, and the final image a jpeg file.
public class QuickImage : Imager { public QuickImage() {} public Image getImage() { return new Bitmap ("Box.gif"); } } //------------ public class FinalImage :Imager { public FinalImage() {} public Image getImage() { return new Bitmap("flowrtree.jpg"); } }
When you go to fetch an image, you initially get the quick image, and after five seconds, if you call the method again, you get the final image. The program’s two states are illustrated in Figure 20-1.
Figure 20-1. The proxy image display on the top is shown until the image loads as shown on the bottom.
Proxies in C#
You see more proxylike behavior in C# than in other languages because it is crafted for network and Internet use. For example, the ADO.NETdatabase connection classes are all effectively proxies.
Copy-on-Write
You can also use proxies to keep copies of large objects that may or may not change. If you create a second instance of an expensive object, a Proxy can decide there is no reason to make a copy yet. It simply uses the original object. Then if the program makes a change in the new copy, the Proxy can copy the original object and make the change in the new instance. This can be a great time and space saver when objects do not always change after they are instantiated.