Open In App
Related Articles

Proxy Design Pattern

Improve
Improve
Improve
Like Article
Like
Save Article
Save
Report issue
Report

The Proxy Design Pattern is a structural design pattern that provides a surrogate or placeholder for another object to control access to it. This pattern is useful when you want to add an extra layer of control over access to an object. The proxy acts as an intermediary, controlling access to the real object.

prox-pattern

A real-world example can be a cheque or credit card as a proxy for what is in our bank account. It can be used in place of cash and provides a means of accessing that cash when required.

  • And that’s exactly what the Proxy pattern does – ” Controls and manages access to the object they are protecting”.
  • As in the decorator pattern, proxies can be chained together. The client, and each proxy, believe it is delegating messages to the real server:

Chaining of Proxies

Chaining proxies in the Proxy Design Pattern means connecting them in a sequence, where each proxy adds its behavior or checks before passing the request to the next proxy or the real object. It’s like forming a chain of guards, each responsible for a specific task.

changing-of-proxies

Components of Proxy Design Pattern

1. Subject

The Subject is an interface or an abstract class that defines the common interface shared by the RealSubject and Proxy classes. It declares the methods that the Proxy uses to control access to the RealSubject.

  • Declares the common interface for both RealSubject and Proxy.
  • Usually includes the methods that the client code can invoke on the RealSubject and the Proxy.

2. RealSubject

The RealSubject is the actual object that the Proxy represents. It contains the real implementation of the business logic or the resource that the client code wants to access.

  • It Implements the operations declared by the Subject interface.
  • Represents the real resource or object that the Proxy controls access to.

3. Proxy

The Proxy acts as a surrogate or placeholder for the RealSubject. It controls access to the real object and may provide additional functionality such as lazy loading, access control, or logging.

  • Implements the same interface as the RealSubject (Subject).
  • Maintains a reference to the RealSubject.
  • Controls access to the RealSubject, adding additional logic if necessary.

Proxy Design Pattern example

Consider a scenario where your application needs to load and display images, and you want to optimize the image loading process. Loading images from disk or other external sources can be resource-intensive, especially if the images are large or stored remotely.

To address this issue, we need to implement the Proxy Design Pattern to control the access and loading of images.

proxypatternclassdiagram

1. Subject (Image Interface):

The Image interface declares the common methods for displaying images, acting as a blueprint for both the real and proxy objects. In this design, it defines the display() method that both RealImage and ProxyImage must implement. This ensures a uniform interface for clients interacting with image objects.

Java

// Subject
interface Image {
    void display();
}

                    

2. RealSubject (RealImage Class):

The RealImage class represents the real object that the proxy will control access to.

  • It implements the Image interface, providing concrete implementations for loading and displaying images from disk.
  • The constructor initializes the image file name, and the display() method is responsible for loading the image if not already loaded and then displaying it.

Java

// RealSubject
class RealImage implements Image {
    private String filename;
 
    public RealImage(String filename) {
        this.filename = filename;
        loadImageFromDisk();
    }
 
    private void loadImageFromDisk() {
        System.out.println("Loading image: " + filename);
    }
 
    public void display() {
        System.out.println("Displaying image: " + filename);
    }
}

                    

3. Proxy (ProxyImage Class):

The ProxyImage class acts as a surrogate for the RealImage. It also implements the Image interface, maintaining a reference to the real image object.

  • The display() method in the proxy checks whether the real image has been loaded; if not, it creates a new instance of RealImage and delegates the display() call to it.
  • This lazy loading mechanism ensures that the real image is loaded only when necessary.

Java

// Proxy
class ProxyImage implements Image {
    private RealImage realImage;
    private String filename;
 
    public ProxyImage(String filename) {
        this.filename = filename;
    }
 
    public void display() {
        if (realImage == null) {
            realImage = new RealImage(filename);
        }
        realImage.display();
    }
}

                    

4. Client Code:

The client code (ProxyPatternExample) demonstrates the usage of the Proxy Design Pattern. It creates an Image object, which is actually an instance of ProxyImage.

  • The client invokes the display() method on the proxy.
  • The proxy, in turn, controls access to the real image, ensuring that it is loaded from disk only when needed.
  • Subsequent calls to display() use the cached image in the proxy, avoiding redundant loading and improving performance.

Java

// Client code
public class ProxyPatternExample {
    public static void main(String[] args) {
        Image image = new ProxyImage("example.jpg");
 
        // Image will be loaded from disk only when display() is called
        image.display();
 
        // Image will not be loaded again, as it has been cached in the Proxy
        image.display();
    }
}

                    

5. Complete Code of the above example:

This code demonstrates how the Proxy Pattern efficiently manages the loading and displaying of images by introducing a proxy that controls access to the real image object, providing additional functionality such as lazy loading.

Java

// Subject
interface Image {
    void display();
}
 
// RealSubject
class RealImage implements Image {
    private String filename;
 
    public RealImage(String filename) {
        this.filename = filename;
        loadImageFromDisk();
    }
 
    private void loadImageFromDisk() {
        System.out.println("Loading image: " + filename);
    }
 
    public void display() {
        System.out.println("Displaying image: " + filename);
    }
}
 
// Proxy
class ProxyImage implements Image {
    private RealImage realImage;
    private String filename;
 
    public ProxyImage(String filename) {
        this.filename = filename;
    }
 
    public void display() {
        if (realImage == null) {
            realImage = new RealImage(filename);
        }
        realImage.display();
    }
}
 
// Client code
public class ProxyPatternExample {
    public static void main(String[] args) {
        Image image = new ProxyImage("example.jpg");
 
        // Image will be loaded from disk only when display() is called
        image.display();
 
        // Image will not be loaded again, as it has been cached in the Proxy
        image.display();
    }
}

                    

Output

Loading image: example.jpg
Displaying image: example.jpg
Displaying image: example.jpg

                    

Why do we need Proxy Design Pattern?

The Proxy Design Pattern is employed to address various concerns and scenarios in software development, providing a way to control access to objects, add functionality, or optimize performance.

  • Lazy Loading:
    • One of the primary use cases for proxies is lazy loading. In situations where creating or initializing an object is resource-intensive, the proxy delays the creation of the real object until it is actually needed.
    • This can lead to improved performance by avoiding unnecessary resource allocation.
  • Access Control:
    • Proxies can enforce access control policies.
    • By acting as a gatekeeper to the real object, proxies can restrict access based on certain conditions, providing security or permission checks.
  • Protection Proxy:
    • Protection proxies control access to a real object by adding an additional layer of security checks.
    • They can ensure that the client code has the necessary permissions before allowing access to the real object.
  • Caching:
    • Proxies can implement caching mechanisms to store results or resources.
    • This is particularly useful when repeated operations on a real object can be optimized by caching previous results, avoiding redundant computations or data fetching.
  • Logging and Monitoring:
    • Proxies provide a convenient point to add logging or monitoring functionalities.
    • By intercepting method calls to the real object, proxies can log information, track usage, or measure performance without modifying the real object.

When to use Proxy Design Pattern?

  • Deferred Object Creation:
    • Use a proxy when you want to postpone the creation of a resource-intensive object until it’s actually needed.
    • This helps in optimizing the application’s startup time and resource usage.
  • Access Control and Permissions:
    • Use a proxy when you need to control and manage access to an object, ensuring that certain conditions or permissions are met before allowing clients to interact with the real object.
    • This is particularly useful for enforcing security measures.
  • Resource Optimization:
    • Use a proxy to optimize the utilization of resources, such as caching results or storing previously fetched data.
    • This can lead to performance improvements by avoiding redundant computations or data retrieval.
  • Remote Object Interaction:
    • Use a proxy when dealing with distributed systems and you want to interact with objects located in different addresses or systems.
    • The proxy can handle the communication details, making remote object interaction more seamless.

When not to use Proxy Design Pattern?

  • Overhead for Simple Operations: Avoid using a proxy for simple objects or operations that don’t involve resource-intensive tasks. Introducing a proxy might add unnecessary complexity in such cases.
  • Unnecessary Abstraction: If your application doesn’t require lazy loading, access control, or additional functionalities provided by proxies, introducing proxies may lead to unnecessary abstraction and code complexity.
  • Performance Impact: If the introduction of a proxy negatively impacts performance rather than improving it, especially in cases where objects are lightweight and creation is not a significant overhead.
  • When Access Control Isn’t Needed: If there are no access control requirements and the client code can directly interact with the real object without any restrictions.
  • When Eager Loading is Acceptable: If eager loading of objects is acceptable and doesn’t affect the performance of the system, introducing a proxy for lazy loading might be unnecessary.




Last Updated : 06 Feb, 2024
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads