1. Overview

The Proxy pattern allows us to create an intermediary that acts as an interface to another resource, while also hiding the underlying complexity of the component.

2. Proxy Pattern Example

Consider a heavy Java object (like a JDBC connection or a SessionFactory) that requires some initial configuration.

We only want such objects to be initialized on demand, and once they are, we’d want to reuse them for all calls:

MrvrsH6

Let’s now create a simple interface and the configuration for this object:

public interface ExpensiveObject {
    void process();
}

And the implementation of this interface with a large initial configuration:

public class ExpensiveObjectImpl implements ExpensiveObject {

    public ExpensiveObjectImpl() {
        heavyInitialConfiguration();
    }
    
    @Override
    public void process() {
        LOG.info("processing complete.");
    }
    
    private void heavyInitialConfiguration() {
        LOG.info("Loading initial configuration...");
    }
    
}

We’ll now utilize the Proxy pattern and initialize our object on demand:

public class ExpensiveObjectProxy implements ExpensiveObject {
    private static ExpensiveObject object;

    @Override
    public void process() {
        if (object == null) {
            object = new ExpensiveObjectImpl();
        }
        object.process();
    }
}

Whenever our client calls the process() method, they’ll just get to see the processing and the initial configuration will always remain hidden:

public static void main(String[] args) {
    ExpensiveObject object = new ExpensiveObjectProxy();
    object.process();
    object.process();
}

Note that we’re calling the process() method twice. Behind the scenes, the settings part will occur only once – when the object is first initialized.

For every other subsequent call, this pattern will skip the initial configuration, and only processing will occur:

Loading initial configuration...
processing complete.
processing complete.

3. When to Use Proxy

Understanding how to use a pattern is important.

Understanding when to use it is critical.

Let’s talk about when to use the Proxy pattern:

  • When we want a simplified version of a complex or heavy object. In this case, we may represent it with a skeleton object which loads the original object on demand, also called as lazy initialization. This is known as the Virtual Proxy
  • When the original object is present in different address space, and we want to represent it locally. We can create a proxy which does all the necessary boilerplate stuff like creating and maintaining the connection, encoding, decoding, etc., while the client accesses it as it was present in their local address space. This is called the Remote Proxy
  • When we want to add a layer of security to the original underlying object to provide controlled access based on access rights of the client. This is called Protection Proxy

4. Conclusion

In this article, we had a look at the proxy design pattern. This is a good choice in the following cases:

  • When we want to have a simplified version of an object or access the object more securely
  • When we want a local version of a remote object

The full source code for this example is available over on GitHub.