Using Proxy Design Pattern In Java
Today, I will discuss another structural design pattern - Proxy Design Pattern. Proxy means an object functioning as another object.
Join the DZone community and get the full member experience.
Join For FreeToday, I will discuss another structural design pattern - Proxy Design Pattern. Proxy means an object functioning as another object.
Proxy Design Pattern
- The Proxy Design Pattern is a Structural Design Pattern and one of the Gang of Four design patterns.
- The Proxy Design Pattern gives a way to create a class that represents the functionality of another class.
- The Proxy could interface to anything; a network connection, a large object in memory, a file, or some other resource that is expensive or impossible to duplicate.
- The Proxy is like a wrapper object that is being called by the client to access the real serving object behind the scenes.
- The Proxy object hides the original object and control access to it.
- The Proxy is used when we may like to create a class that can perform as an interface to something else.
- The Proxy pattern allows us to create objects having an original object to interface their functionality to the client application.
- The Proxy is most commonly used in the implementation of the lazy loading of objects. I mean creating proxy objects in place of full real objects until it is actually needed.
- The Proxy can also be used to add an additional layer of security around the real object.
- The Proxy hides the Real object by providing a virtual and customized implementation.
- We can do proxy in many ways like:
- Virtual Proxy - Do lazy loading of memory rich or heavy objects until it is needed.
- Decorative Proxy - Add extra functionality to the existing objects just like we do in Decorator Design Pattern.
- Protective Proxy - Control access to the objects functionality.
- Debugging Proxy - Add logs that may also be helpful in debugging.
- Remote Proxy - provides a local representative for a remote object like stub objects in RMI/RPC or CORBA.
- Smart Proxy - checking the lock on real object while updating, loading persistence object upon the first reference, managing real object reference, etc.
- Client Application can use the proxy object similar to the real object because both implement the same interface.
- Proxy Design Pattern uses three components to implement:
- Subject - the interface which exposes the functionality.
- Real Subject - the class implements the Subject and provides the concrete implementation of the interface. In this class, we hide behind the Proxy.
- Proxy - the class implements the Subject so that it can substitute Real Subject objects. It maintains the reference of the Real Subject to the substituted Proxy object so that it can forward a request to the Real Subject whenever needed.
Let's take an example to understand the proxy.
Image Loading Application Using Proxy Design Pattern
Suppose we have an Image interface to load and operate images as below:
package org.trishinfotech.proxy;
public interface Image {
public void load();
public void show();
public void showSummary();
public void resize();
public void remove();
public void close();
}
Now there is a concrete class named as RealImage to deal with local image files:
xxxxxxxxxx
package org.trishinfotech.proxy;
public class RealImage implements Image {
protected String fileNameWithPath;
public RealImage(String fileNameWithPath) {
this.fileNameWithPath = fileNameWithPath;
load();
}
public void load() {
System.out.println("RealImage: Loading image: " + fileNameWithPath);
}
public void show() {
System.out.println("RealImage: Showing image: " + fileNameWithPath);
}
public void showSummary() {
System.out.println("RealImage: Showing Summary of image: " + fileNameWithPath);
}
public void resize() {
System.out.println("RealImage: Resizing image: " + fileNameWithPath);
}
public void remove() {
System.out.println("RealImage: Removing image: " + fileNameWithPath);
}
public void close() {
System.out.println("RealImage: Closing image: " + fileNameWithPath);
}
public String getFileNameWithPath() {
return fileNameWithPath;
}
public void setFileNameWithPath(String fileNameWithPath) {
this.fileNameWithPath = fileNameWithPath;
}
}
As you can see that the class loads the image while we create the object (early loading).
Now there is another concrete class named as RemoteImage to deal with images stored in the shared drive on the network.
xxxxxxxxxx
package org.trishinfotech.proxy;
public class RemoteImage implements Image {
private String remoteHost;
protected String fileNameWithPath;
public RemoteImage(String remoteHost, String fileNameWithPath) {
this.remoteHost = remoteHost;
this.fileNameWithPath = fileNameWithPath;
load();
}
public void load() {
System.out.printf("RemoteImage: Loading image: 'smb:\\\\%s\\%s'.\n", remoteHost, fileNameWithPath);
}
public void show() {
System.out.printf("RemoteImage: Showing image: 'smb:\\\\%s\\%s'.\n", remoteHost, fileNameWithPath);
}
public void showSummary() {
System.out.printf("RemoteImage: Showing Summary of image: 'smb:\\\\%s\\%s'.\n", remoteHost, fileNameWithPath);
}
public void resize() {
System.out.printf("RemoteImage: Resizing image: 'smb:\\\\%s\\%s'.\n", remoteHost, fileNameWithPath);
}
public void remove() {
System.out.printf("RemoteImage: Removing image: 'smb:\\\\%s\\%s'.\n", remoteHost, fileNameWithPath);
}
public void close() {
System.out.printf("RemoteImage: Closing image: 'smb:\\\\%s\\%s'.\n", remoteHost, fileNameWithPath);
}
public String getRemoteHost() {
return remoteHost;
}
public void setRemoteHost(String remoteHost) {
this.remoteHost = remoteHost;
}
public String getFileNameWithPath() {
return fileNameWithPath;
}
public void setFileNameWithPath(String fileNameWithPath) {
this.fileNameWithPath = fileNameWithPath;
}
}
Please note that both class are sub-class of Image interface.
Now lets create a proxy class to address:
- Lazy loading of image.
- Adding security layer for resize and remove of the image.
- Adding logs and error statements while dealing with images.
Since I have two concrete classes of Image interface, I am writing a common proxy class named ProxyImage to deal with both. We can write separate proxies as well. As I mentioned already, Proxy class we make sub-class of same interface or class. i.e. Image in this case.
xxxxxxxxxx
package org.trishinfotech.proxy;
public class ProxyImage implements Image {
protected String remoteHost;
protected String fileNameWithPath;
private Image image;
protected boolean isAdmin;
public ProxyImage(String fileNameWithPath, boolean isAdmin) {
super();
this.fileNameWithPath = fileNameWithPath;
this.isAdmin = isAdmin;
}
public ProxyImage(String remoteHost, String fileNameWithPath, boolean isAdmin) {
this(fileNameWithPath, isAdmin);
this.remoteHost = remoteHost;
}
public void load() {
if (image == null) {
if (remoteHost != null) {
image = new RemoteImage(remoteHost, fileNameWithPath);
} else {
image = new RealImage(fileNameWithPath);
}
} else {
System.err.printf("ImageProxy: Already loaded image: '%s'.\n", fileNameWithPath);
}
}
public void show() {
load();
image.show();
}
public void showSummary() {
System.err.printf("ImageProxy: Showing Summary of image: '%s'.\n", fileNameWithPath);
}
public void resize() {
if (isAdmin) {
load();
image.resize();
} else {
System.err.printf("ImageProxy: Only Admin can resize image: '%s'.\n", fileNameWithPath);
}
}
public void remove() {
if (isAdmin) {
load();
image.resize();
} else {
System.err.printf("ImageProxy: Only Admin can remove image: '%s'.\n", fileNameWithPath);
}
}
public void close() {
if (image != null) {
image.close();
} else {
System.err.printf("ImageProxy: Already closed image: " + fileNameWithPath);
}
}
public String getRemoteHost() {
return remoteHost;
}
public void setRemoteHost(String remoteHost) {
this.remoteHost = remoteHost;
}
public String getFileNameWithPath() {
return fileNameWithPath;
}
public void setFileNameWithPath(String fileNameWithPath) {
this.fileNameWithPath = fileNameWithPath;
}
public boolean isAdmin() {
return isAdmin;
}
public void setAdmin(boolean isAdmin) {
this.isAdmin = isAdmin;
}
}
Now lets write a Main class to execute and test our code:
xxxxxxxxxx
package org.trishinfotech.proxy;
public class Main {
public static void main(String[] args) {
System.out.println("Using Real Image class...");
Image image1 = new RealImage("ABC.jpg");
image1.showSummary();
image1.load();
image1.show();
image1.resize();
image1.close();
image1.remove();
System.out.println("------------------------------------------------");
System.out.println("Using Proxy Image class...");
Image image2 = new ProxyImage("ABC.jpg", true);
image2.showSummary();
image2.load();
image2.show();
image2.resize();
image2.close();
image2.remove();
System.out.println("------------------------------------------------");
System.out.println("Using Remote Image class...");
Image image3 = new RemoteImage("192.168.0.1", "ABC.jpg");
image3.showSummary();
image3.load();
image3.show();
image3.resize();
image3.close();
image3.remove();
System.out.println("------------------------------------------------");
System.out.println("Using Proxy Image class...");
Image image4 = new ProxyImage("192.168.0.1", "ABC.jpg", false);
image4.showSummary();
image4.load();
image4.show();
image4.resize();
image4.close();
image4.remove();
System.out.println("------------------------------------------------");
}
}
And below is the output of the program:
xxxxxxxxxx
Using Real Image class...
RealImage: Loading image: ABC.jpg
RealImage: Showing Summary of image: ABC.jpg
RealImage: Loading image: ABC.jpg
RealImage: Showing image: ABC.jpg
RealImage: Resizing image: ABC.jpg
RealImage: Closing image: ABC.jpg
RealImage: Removing image: ABC.jpg
------------------------------------------------
Using Proxy Image class...
ImageProxy: Showing Summary of image: 'ABC.jpg'.
RealImage: Loading image: ABC.jpg
ImageProxy: Already loaded image: 'ABC.jpg'.
ImageProxy: Already loaded image: 'ABC.jpg'.
ImageProxy: Already loaded image: 'ABC.jpg'.
RealImage: Showing image: ABC.jpg
RealImage: Resizing image: ABC.jpg
RealImage: Closing image: ABC.jpg
RealImage: Resizing image: ABC.jpg
------------------------------------------------
Using Remote Image class...
RemoteImage: Loading image: 'smb:\\192.168.0.1\ABC.jpg'.
RemoteImage: Showing Summary of image: 'smb:\\192.168.0.1\ABC.jpg'.
RemoteImage: Loading image: 'smb:\\192.168.0.1\ABC.jpg'.
RemoteImage: Showing image: 'smb:\\192.168.0.1\ABC.jpg'.
RemoteImage: Resizing image: 'smb:\\192.168.0.1\ABC.jpg'.
RemoteImage: Closing image: 'smb:\\192.168.0.1\ABC.jpg'.
RemoteImage: Removing image: 'smb:\\192.168.0.1\ABC.jpg'.
------------------------------------------------
Using Proxy Image class...
ImageProxy: Showing Summary of image: 'ABC.jpg'.
RemoteImage: Loading image: 'smb:\\192.168.0.1\ABC.jpg'.
ImageProxy: Already loaded image: 'ABC.jpg'.
RemoteImage: Showing image: 'smb:\\192.168.0.1\ABC.jpg'.
ImageProxy: Only Admin can resize image: 'ABC.jpg'.
RemoteImage: Closing image: 'smb:\\192.168.0.1\ABC.jpg'.
ImageProxy: Only Admin can remove image: 'ABC.jpg'.
------------------------------------------------
The Source Code can be found here: Proxy-Design-Pattern-Sample-Code
I hope this tutorial demonstrates the use of the proxy design pattern.
Liked the article? Please don't forget to press that like button. Happy coding!
Need more articles, please visit my profile: Brijesh Saxena
Opinions expressed by DZone contributors are their own.
Comments