The Proxy Pattern is a structural design pattern that provides a surrogate or placeholder for another object to control access to it. The proxy typically implements the same interface as the real subject and delegates requests to it while adding extra behaviour like lazy loading, access control, logging, or caching.
Applicability
- When you need to control access to an object (e.g. restrict access, delay initialization, or log usage).
- When creating and using a real object is expensive or resource-intensive (e.g. loading data from a database or a file).
- When you want to add a layer of security, caching, or remote access without modifying the original object
Pros and Cons
Advantages
- Controlled access to the real object
- Can prevent unauthorised access, delay creation, or monitor usage.
- Lazy instantiation
- Resource-heavy objects are only created when needed (virtual proxy).
- Separation of concerns
- Keeps additional behaviour like logging, counting, caching, or access control out of the real subject.
- Supports remote or distributed systems
- Remote proxies can represent objects in different address spaces.
Drawbacks
- Adds complexity
- Increases the number of classes and levels of indirection.
- Can introduce performance overhead
- Especially when proxies perform additional logic before delegating calls.
Approach
- Define a Subject interface with methods that both the proxy and real object will implement.
- Implement a RealSubject class that provides the actual implementation.
- Create a Proxy class that implements the same interface and controls access to the RealSubject.
- The Client interacts with the proxy, which then delegates to the RealSubject when appropriate.
Components
- Subject (interface or abstract class)
- Declares the common interface for RealSubject and Proxy.
- RealSubject
- The actual object that performs real operations.
- Proxy
- Implements the same interface and controls access to the RealSubject.
classDiagram class Subject { <<abstract>> +request()* abstract } class RealSubject { +request() } class Proxy { -real_subject: RealSubject +request() } Subject <|-- RealSubject : implements Subject <|-- Proxy : implements Proxy --> RealSubject : contains
Example
Let’s demonstrate a Virtual Proxy example for image loading. The proxy delays loading an image file until it is actually displayed.
Subject
interface Image {
void display();
}
Real subject
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk();
}
private void loadFromDisk() {
System.out.println("Loading image: " + filename);
}
@Override
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;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(filename); // lazy loading
}
realImage.display();
}
}
Client
public class ProxyPatternExample {
public static void main(String[] args) {
Image image = new ProxyImage("my_photo.jpg");
System.out.println("Image created but not loaded.");
// Image will be loaded only when display() is called
image.display();
// Second time: no need to load from disk again
image.display();
}
}
Image created but not loaded.
Loading image: my_photo.jpg
Displaying image: my_photo.jpg
Displaying image: my_photo.jpg