2.7 Proxy Pattern - Structural Design Pattern

The Proxy Pattern gives another item a stand-in or surrogate to manage access to it. This blog post will examine the Proxy Pattern, explain its composition, and look at an example of Java code that demonstrates how it is used.

What is the Proxy Pattern?

An item known as the genuine subject is accessed through a surrogate or placeholder that is provided by the Proxy Pattern. The primary goal of employing a proxy is to control access to the true subject by adding a layer of indirection. This has multiple applications, including data caching, slow initialization, access control, logging, and more.

Structure of Proxy Pattern

The Structural Proxy Pattern involves the following components:

  1. Subject: This is an interface that defines the common interface for the RealSubject and Proxy classes. It allows the Proxy to be used anywhere the RealSubject is expected.

  2. RealSubject: This is the real object that the Proxy represents. It implements the Subject interface and provides the actual functionality.

  3. Proxy: This is the surrogate class that acts as an intermediary between the client and the RealSubject. It maintains a reference to the RealSubject and controls access to it, optionally performing additional functionality before or after forwarding the request to the RealSubject.

Java Code Example

Let's illustrate the Proxy Pattern with a simple example in Java. Consider a scenario where we have a Image interface representing an image and a RealImage class representing the actual image file. We'll create a ProxyImage class to control access to the RealImage object.

// Subject Interface
interface Image {
    void display();
}
// RealSubject Class
class RealImage implements Image {
    private String filename;
    public RealImage(String filename) {
        this.filename = filename;
        loadImageFromDisk();
    }
    private void loadImageFromDisk() {
        System.out.println("Loading image: " + filename);
    }
    @Override
    public void display() {
        System.out.println("Displaying image: " + filename);
    }
}
// Proxy Class
class ProxyImage implements Image {
    private RealImage realImage;
    private String filename;

    public ProxyImage(String filename) {
        this.filename = filename;
    }
    @Override
    public void display() {
        if (realImage == null) {
            realImage = new RealImage(filename);
        }
        realImage.display();
    }
}
// Client Class
public class ProxyPatternExample {
    public static void main(String[] args) {
        // Client uses Proxy to access RealSubject
        Image image1 = new ProxyImage("test1.jpg");
        Image image2 = new ProxyImage("test2.jpg");
        // Image 1 will be loaded from disk
        image1.display();
        // Image 1 will be displayed without loading from disk again
        image1.display();
        // Image 2 will be loaded from disk
        image2.display();
    }
}

In this example, RealImage represents the actual image file, and ProxyImage controls access to it. The first time an image is displayed, it is loaded from the disk, and subsequent display requests for the same image are served from memory, demonstrating lazy loading behavior.

Output:

Loading image: test1.jpg

Displaying image: test1.jpg

Displaying image: test1.jpg

Loading image: test2.jpg

Displaying image: test2.jpg

Conclusion

Access to objects can be managed in a flexible manner with the Structural Proxy Pattern, enabling the transparent integration of new functionality. You can improve the behavior of the original object without modifying its code by employing a proxy. Java applications can benefit from more modular, maintainable, and effective code if the Proxy Pattern is understood and used.