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:
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.