Skip to content

Unveiling the Proxy Design Pattern in Java

Posted on:September 10, 2022 at 03:13 PM

The Proxy design pattern is a structural design pattern that allows us to have an intermediary or proxy object in front of another object or functionality. This intermediary object provides the same interface as the original object and allows us to add some additional functionality before and after accessing the original object. This is immensely useful when we want to optimize access to an object or perform certain operations around the object’s functionality.

To better understand this concept, let’s delve into a simple example.

Table of contents

Open Table of contents

Sections

A Disk Functionality Scenario

Imagine we have a disk functionality that involves significant processing power. Accessing the disk frequently can potentially slow down our system, so we want to access it only when absolutely necessary. This is where the Proxy pattern comes into play.

By creating a proxy in front of the disk functionality, we can introduce caching. If a client requests a file, the proxy can first check if it’s available in the cache and provide it from there instead of accessing the disk directly. This way, we optimize our system’s efficiency and improve performance.

proxyUML

Comparison with Other Patterns

Facade Pattern:

The Facade pattern simplifies access to a specific functionality by providing a unified interface. However, it abstracts the underlying system’s complexity. On the other hand, the Proxy pattern maintains the same interface as the original object and may add functionality before and after the access.

Composite Pattern:

Similar to the Composite pattern, the Proxy pattern allows us to manage the lifecycle of the object it represents. The key difference is that the Composite pattern primarily focuses on accessing the functionality of the underlying object, whereas the Proxy pattern also manages the object’s lifecycle.

Implementing the Proxy Pattern

Let’s implement a simple program to demonstrate the Proxy pattern. Suppose we have an external library that provides image functionality, and we want to add caching and logging before and after accessing this functionality.

Java Implementation

Interface for Image

interface Image {
    void display();
}

Real Image Class

class RealImage implements Image {
    private String fileName;

    public RealImage(String fileName) {
        this.fileName = fileName;
        loadFromDisk();
    }

    private void loadFromDisk() {
        System.out.println("Real Image: Loading " + fileName);
    }

    @Override
    public void display() {
        System.out.println("Real Image: Displaying " + fileName);
    }
}

Proxy Image 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);
        }
        System.out.println("Proxy Image: Displaying " + fileName);
    }
}

In this example, the ProxyImage class acts as a proxy for RealImage. It maintains the same interface as Image, allowing the client to access the image.

Usage Example

public class Client {
    public static void main(String[] args) {
        Image image = new ProxyImage("test.png");

        // Image loaded from disk
        image.display();

        // Image fetched from proxy (not loaded again from disk)
        image.display();
    }
}

Conclusion

The Proxy design pattern is a powerful tool when it comes to optimizing access to an object or adding functionality around its usage. By maintaining the same interface and adding additional behavior, we can enhance efficiency and control the object’s lifecycle effectively. Understanding and utilizing the Proxy pattern can significantly contribute to creating robust and efficient software systems.

Happy coding!