Proxy Design Pattern

In Proxy pattern, a proxy object represents a placeholder or surrogate which provides an interface to outer world to access the functionality of original object. A proxy object is simply means an object representing another object. Proxies are also known as handles, surrogates, and wrappers. This type of design pattern comes under structural design pattern.
This pattern is useful in scenarios where direct access to an object may be too costly or not appropriate, and a surrogate is needed to manage access and interactions. In this tutorial, we will explore the Proxy Design Pattern in Java, covering its structure, implementation, best practices, and advantages.


Structure of the Proxy Design Pattern

The Proxy Design Pattern consists of the following components:
  • Subject Interface : This interface declares the common interface for the RealSubject and Proxy classes. It defines the operations that the RealSubject and Proxy classes must implement.

  • RealSubject : The RealSubject is the real object that the proxy represents. It implements the operations defined in the Subject interface. Clients interact with the RealSubject through the proxy.

  • Proxy : The Proxy class implements the Subject interface and acts as a surrogate for the RealSubject. It controls access to the RealSubject, allowing it to add additional functionality, such as lazy loading, access control, or logging.

  • Client : The Client is the class that interacts with the Proxy to access the RealSubject. The Client is unaware of whether it is dealing with the RealSubject or a Proxy.

Usage of Proxy Pattern

There are four situations in which the Proxy pattern is used.
  • Virtual proxy
    Suppose, you want to access a huge file from a database. Since, initialization of a database client is expensive operation, we will use proxy pattern to instantiate an instance of database client on first database request by client. After the first request, proxy will reuse the database client for any future requests by client instead of creating a new instance of database client everytime. This will reduce the duplication of object, reduce latency to access data from database and save memory.

  • Remote proxy
    A remote proxy of a remote resource (like a web services) provides a local interface of the remote resource at different address location. A client can use the interface provided by remote proxy or a remote resources to access the functionalities of a remove resource. Talking to the remote resource might involve serialization and deserialization of data, all such logic can be encapsulated in remote proxies and the client application need not worry about their implementation. Examples of remote proxies include a proxy of REST service or aws S3.

  • Smart Proxy
    A smart proxy can provide some additional functionalities to access or optimize the interation between client and resource like splitting a large request to access 100 images into 5 requests of 20 images each. Other uses of smart proxies include to provide additional security, to provide failure handling in case or any problem while accessing resource etc.

  • Protection proxy
    A protection proxy is used to enforce access control to a resource. It acts as an authorization layer to verify that whether client has access necessary access the appropriate resource or not. If client have appropriate access then it will forward the client's request to the resource else block the unauthourized client request from accessing the resource.

Advantages of Proxy Design Pattern

  • Controlled Access : Being able to manage who has access to the actual item is the main benefit of the proxy approach. Access control measures can be implemented by proxies, which can grant or deny access depending on predetermined criteria.

  • Reduced Resource Usage : Proxies help to reduce resource utilization by holding off on creating or loading the original object unless it's absolutely necessary. This is useful in situations where resource optimization is required.

  • Enhanced Security : Access control, authentication, and authorization are just a few of the security features that may be put in place with proxies. By guaranteeing that only approved users or components can access particular functionality, this improves system security.

  • Lazy Loading : Using proxies, real objects are only loaded when necessary, hence enabling lazy loading. When working with procedures or objects that require a lot of resources, this is quite helpful.

  • Logging and Auditing : Proxies are useful for logging and auditing purposes. They can capture and log information before or after forwarding requests to the real object, providing insights into the system's behavior.

  • Simplified Client Code : Simplified client code is frequently the result of using proxies. Clients do not need to be aware of the extra functionality or control that proxies bring; they can interact with them in the same manner that they interact with real objects.

  • Dynamic Proxies for Flexibility : Dynamic proxies offer flexibility by allowing the creation of proxies at runtime. This is particularly useful when the structure of the real object is known only at runtime, eliminating the need for explicit proxy classes.

  • Remote Proxies : Clients can interact with remotely situated items as though they were local by using proxies to represent them. Clients can see through the transparent communication details handled by remote proxies.

Implementation of Proxy Design Pattern

In below mentioned example, we will create a proxy class called ImageManagerProxy which provides an interface to outer world to access the functionalities of ImageManager class.
First of all we will create an ImageServer interface and it's concrete implementation as ImageManager.

Proxy Design Pattern UML Diagram ImageServer.java
  • void displayImage(String imageName) : Downloads image file and display it on screen.
  • void uploadImage(String imageName): Uploads image file to server.
public interface ImageServer {
    void displayImage(String imageName);
    void uploadImage(String imageName);
}

ImageManager.java
public class ImageManager implements ImageServer {

    @Override
    public void displayImage(String imageName) {
        System.out.println("Downloading image file" + imageName);
        System.out.println("Displaying Image" + imageName);
    }

    @Override
    public void uploadImage(String imageName) {
        System.out.println("Uploading image file" + imageName);
    }
}

ImageManagerProxy.java
ImageManagerProxy class implements the same interface ImageServer and provides access to functionalities of ImageManager to external world.
public class ImageManagerProxy implements ImageServer {
    private ImageManager imageManager;

    @Override
    public void displayImage(String imageName) {
        if(imageManager == null) {
            imageManager = new ImageManager();
        }
        imageManager.displayImage(imageName);
    }

    @Override
    public void uploadImage(String imageName) {
        System.out.println("Compressing image file" + imageName);
        if(imageManager == null) {
            imageManager = new ImageManager();
        }
        imageManager.uploadImage(imageName);
    }
}

ProxyPatternExample.java
ProxyPatternExample will use ImageManagerProxy to invoke ImageManager instead of calling ImageManager directly.
public class ProxyPatternExample {
    public static void main(String[] args) {
        ImageManagerProxy imageManagerProxy = new ImageManagerProxy();
        // Upload image using proxy
        imageManagerProxy.uploadImage("Profile_pic.jpg");
        // Display image using roxy
        imageManagerProxy.displayImage("Profile_pic.jpg");
    }
}

Output

Compressing image fileProfile_pic.jpg
Uploading image fileProfile_pic.jpg
Downloading image fileProfile_pic.jpg
Displaying ImageProfile_pic.jpg

Important Point about Proxy Pattern
Here are the differences between Proxy pattern and other related patterns.
  • A Proxy provides the same interfacte as original object.

  • An Adapter provides a different interface of the original object for compatibilty.

  • A Decorator provides an enhanced interface by adding for additional features on top of the features provided by original object.

Best Practices of Proxy Design Pattern

Following best practices that support a manageable and effective implementation is crucial to getting the most out of the Proxy Design Pattern:
  • Make sure that an interface is implemented consistently by the real subject and the proxy. Because of this, clients can effortlessly transition between using the proxy and the real object without requiring modifications to their client code.

  • Strive to minimize the impact on client code when introducing a proxy. Clients should be able to use the proxy as a drop-in replacement for the real object without significant modifications.

  • Recognize the use scenarios in which a proxy is required. Think about situations like caching, logging, access control, and lazy loading. Taking the requirements into consideration, select the right kind of proxy.

  • Use proxies for access control by implementing logic in the proxy to restrict or grant access to the real object based on certain conditions. This is useful for scenarios where fine-grained control over object access is required.

  • In scenarios where caching is required, implement proxy logic to manage and serve cached data when appropriate. This can improve performance by avoiding redundant operations on the real object.

  • If many threads are accessing proxies concurrently, make sure they are thread-safe. To avoid race situations, think about implementing synchronization techniques or other concurrency management methods.
Related Topics
Iterator Design Pattern
State Design Pattern
Bridge Design Pattern
Prototype Design Pattern
Memento Design Pattern
List of Design Patterns