Introduction
The Proxy Design Pattern is a structural design pattern that provides a surrogate or placeholder for another object to control access to it. This pattern is useful when creating objects is resource-intensive, or when access to the real object needs to be controlled or restricted.
Purpose
The main goal of the Proxy Pattern is to manage access to an object. This can involve delaying the creation of the object until it’s actually needed, adding security checks before access, or logging interactions with the object. Essentially, it allows for additional behavior without modifying the real object’s code.
Common Use Cases
The Proxy Pattern is typically used in the following scenarios:
- When an object is expensive to create, and it is beneficial to delay its creation until it is actually needed (virtual proxy).
- When access to an object should be restricted or controlled based on permissions (protection proxy).
- When an object resides in a different address space or network and needs a representative locally (remote proxy).
- When extra functionality such as logging, reference counting, or caching is required transparently (smart proxy).
Structure
The Proxy Pattern typically consists of three main components:
- A Subject interface that defines the common operations.
- A RealSubject class that contains the core logic and actual functionality.
- A Proxy class that implements the same interface and controls access to the RealSubject.
Example: Protection Proxy
This example demonstrates how to use a proxy to protect access to a document. Only users with the “ADMIN” role are allowed to open and view the document.
Step 1: Subject Interface
public interface Document {
void display();
}
Step 2: RealSubject Class
public class RealDocument implements Document {
private String filename;
public RealDocument(String filename) {
this.filename = filename;
loadFromDisk();
}
private void loadFromDisk() {
System.out.println("Loading document: " + filename);
}
@Override
public void display() {
System.out.println("Displaying: " + filename);
}
}
Step 3: Proxy Class
public class DocumentProxy implements Document {
private RealDocument realDocument;
private String filename;
private String userRole;
public DocumentProxy(String filename, String userRole) {
this.filename = filename;
this.userRole = userRole;
}
@Override
public void display() {
if ("ADMIN".equals(userRole)) {
if (realDocument == null) {
realDocument = new RealDocument(filename);
}
realDocument.display();
} else {
System.out.println("Access denied: Only ADMIN can view the document.");
}
}
}
Step 4: Client Code
public class Main {
public static void main(String[] args) {
Document doc1 = new DocumentProxy("secret.pdf", "USER");
doc1.display();
System.out.println("---");
Document doc2 = new DocumentProxy("secret.pdf", "ADMIN");
doc2.display();
doc2.display();
}
}
Output
Access denied: Only ADMIN can view the document.
---
Loading document: secret.pdf
Displaying: secret.pdf
Displaying: secret.pdf
Advantages
The Proxy Pattern provides several advantages. It allows lazy instantiation of objects, which saves system resources. It also enables control over access to the object, helping to enforce security policies. Additionally, proxies can add functionalities like logging or caching without modifying the real object’s code.
Disadvantages
The main drawback of the Proxy Pattern is the added layer of abstraction, which may introduce complexity. In certain cases, using a proxy may lead to performance overhead or confusion if not clearly documented.
Conclusion
The Proxy Design Pattern is a powerful tool in situations where object creation is expensive or where access to an object needs to be managed or extended. It offers a clean, structured way to control and enhance the behavior of real objects without changing their code. In Java, proxies are particularly useful for adding security checks, lazy loading, and access control in real-world applications.