Virtual Proxy

The Virtual Proxy acts as a representative for an object that may be expensive to create. The Virtual Proxy often defers the creation of the object until it is needed; the Virtual Proxy also acts as a surrogate for the object before and while it is being created. After that, the proxy delegates requests directly to the RealSubject.

Designing Virtual Proxy Example

Implementation

Creating ImageProxy:

import java.net.*;
import java.awt.*;
import javax.swing.*;
 
class ImageProxy implements Icon {
	/* The imageIcon is the REAL icon that we eventually want to display when loaded. */
	volatile ImageIcon imageIcon;
	final URL imageURL;
	Thread retrievalThread;
	boolean retrieving = false;
	
	/* We pass the URL of the image. This is the image to display when loaded */
	public ImageProxy(URL url) { imageURL = url; }
	
	/* We return a default width and height until the image is loaded */
	public int getIconWidth() {
		if (imageIcon != null) {
			return imageIcon.getIconWidth();
		} else {
			return 800;
		}
	}
	public int getIconHeight() {
		if (imageIcon != null) {
			return imageIcon.getIconHeight();
		} else {
			return 600;
		}
	}
	
	/* Used by two different threads. With volatile (to protect reads), we use a synchronized setter (to protect writes) */
	synchronized void setImageIcon(ImageIcon imageIcon) {
		this.imageIcon = imageIcon;
	}
 
	public void paintIcon(final Component c, Graphics g, int x, int y) {
		if (imageIcon != null) {
			/* If we've got an icon, tell it to paint itself */
			imageIcon.paintIcon(c, g, x, y);
		} else {
			/* Otherwise, we display the loading message */
			g.drawString("Loading album cover, please wait...", x+300, y+190);
			
			/* If we aren't already trying to load */
			if (!retrieving) {
				/* Only one thread calls paint, so it is safe. */
				retrieving = true;
				
				/* Use another thread to retrieve the image. */
				retrievalThread = new Thread(new Runnable() {
					public void run() {
						try {
							/* In thread, we instantiate the Icon object. Its constructor will not return until the image is loaded*/
							setImageIcon(new ImageIcon(imageURL, "Album Cover"));
							/* Repaint once image is loaded. */
							c.repaint();
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				});
 
				retrievalThread.start();	
			}
		}
	}
}