Last Updated on September 24, 2023 by KnownSense
The Proxy pattern is a structural design pattern that allows an object (the proxy) to act as an intermediary or placeholder for another object, controlling access to it. It is often used to add a level of control or optimization to the interactions with the real object, such as lazy loading, access control, or logging. It allowing you to perform something either before or after the request gets through to the original object.
Imagine you have a computer with a massive file folder that contains all your important documents and files. You want to add some control and optimization to how people access these files, and you can achieve this using the Proxy pattern.
Imagine you have a computer with a massive file folder that contains all your important documents and files. You want to add some control and optimization to how people access these files, and you can achieve this using the Proxy pattern.
Benefits
- Control: Proxies provide a level of control over access to an object. They can add validation, security checks, and access control mechanisms to ensure that clients use the real object in a safe and controlled manner.
- Lazy Loading: Virtual proxies can delay the creation or loading of resource-intensive objects until they are actually needed. This can significantly improve application performance by deferring the cost of object creation.
- Resource Management: Proxies can manage resources associated with the real object, such as database connections, network connections, or memory usage. This ensures that resources are allocated and released appropriately.
- Caching: Proxies can implement caching mechanisms to store and reuse results or data from the real object, reducing redundant computations or network requests and improving application responsiveness.
- Logging and Monitoring: Proxies can capture and log interactions with the real object, allowing for monitoring, debugging, or auditing purposes without modifying the real object’s code.
- Access Control: Protection proxies can enforce access control rules, ensuring that only authorized clients can use the real object. This enhances security by restricting access to sensitive functionality or data.
- Remote Access: Remote proxies enable clients to interact with objects located on remote servers or different address spaces, abstracting the complexities of network communication. This is valuable for distributed systems and remote service access.
- Interface Adaptation: Proxies can adapt the interface of the real object, making it easier for clients to work with the object by providing a simpler or more specialized interface. This can also help in versioning or backward compatibility scenarios.
- Performance Optimization: Proxies can implement performance optimizations, such as caching frequently used results or implementing lazy loading, to enhance system performance and responsiveness.
- Separation of Concerns: The Proxy pattern promotes the separation of concerns by isolating the client’s interactions with the real object. This separation simplifies the client code and makes it easier to maintain and extend.
- Testing and Mocking: Proxies can be useful in testing scenarios. Mock objects can act as proxies to simulate the behavior of real objects, making it easier to isolate and test specific parts of an application.
- Resource Sharing: Proxies can manage resource-sharing scenarios. For example, multiple clients can share a single proxy to access a resource or service, ensuring efficient resource utilization.
UML of Proxy pattern
Implementation
Step1: Create a interface representing the file system
interface FileSystem {
void displayFileContents(String fileName);
}
Step2: Create a concrete class the representing the real file system
class RealFileSystem implements FileSystem {
private String fileName;
public RealFileSystem(String fileName) {
this.fileName = fileName;
loadFile();
}
private void loadFile() {
System.out.println("Loading file: " + fileName);
// Simulate loading file contents from storage.
}
@Override
public void displayFileContents(String fileName) {
System.out.println("Displaying file contents of: " + fileName);
// Display the actual file contents.
}
}
Step3: Create the proxy classes for different purposes like loads the real object on-demand or controls access to sensitive files or interacts with a distant file system on remote server
class VirtualProxy implements FileSystem {
private RealFileSystem realFileSystem;
private String fileName;
public VirtualProxy(String fileName) {
this.fileName = fileName;
}
@Override
public void displayFileContents(String fileName) {
if (realFileSystem == null) {
realFileSystem = new RealFileSystem(fileName);
}
realFileSystem.displayFileContents(fileName);
}
}
class ProtectionProxy implements FileSystem {
private RealFileSystem realFileSystem;
private String fileName;
private boolean authorized;
public ProtectionProxy(String fileName, boolean authorized) {
this.fileName = fileName;
this.authorized = authorized;
}
@Override
public void displayFileContents(String fileName) {
if (authorized) {
if (realFileSystem == null) {
realFileSystem = new RealFileSystem(fileName);
}
realFileSystem.displayFileContents(fileName);
} else {
System.out.println("Access denied. You are not authorized to view this file.");
}
}
}
class RemoteProxy implements FileSystem {
private String remoteServer;
private String fileName;
public RemoteProxy(String remoteServer, String fileName) {
this.remoteServer = remoteServer;
this.fileName = fileName;
}
@Override
public void displayFileContents(String fileName) {
// Simulate remote file access and retrieval.
System.out.println("Fetching file from remote server: " + remoteServer);
System.out.println("Displaying file contents of: " + fileName);
}
}
Step4: In main class use the proxy classes to the read the file system
public class ProxyPatternExample {
public static void main(String[] args) {
// Virtual Proxy
FileSystem virtualProxy = new VirtualProxy("large_file.txt");
virtualProxy.displayFileContents("large_file.txt");
// Protection Proxy
FileSystem protectionProxy = new ProtectionProxy("sensitive_file.txt", true);
protectionProxy.displayFileContents("sensitive_file.txt");
// Trying to access sensitive file without authorization
FileSystem unauthorizedProxy = new ProtectionProxy("sensitive_file.txt", false);
unauthorizedProxy.displayFileContents("sensitive_file.txt");
// Remote Proxy
FileSystem remoteProxy = new RemoteProxy("remote_server", "remote_file.txt");
remoteProxy.displayFileContents("remote_file.txt");
}
}
OUTPUT
Loading file: large_file.txt
Displaying file contents of: large_file.txt
Loading file: sensitive_file.txt
Displaying file contents of: sensitive_file.txt
Access denied. You are not authorized to view this file.
Fetching file from remote server: remote_server
Displaying file contents of: remote_file.txt
Conclusion
In conclusion, the Proxy pattern is a powerful design pattern that acts as an intermediary between a client and a real object, providing numerous benefits such as lazy loading, access control, and resource management. It enhances code modularity, security, and performance by allowing for the dynamic control of object interactions. Whether used for virtualization, protection, or remote access, the Proxy pattern effectively addresses various design challenges while maintaining a clear separation of concerns.