Proxy Pattern
Introduction
The Proxy pattern is a structural design pattern that provides a surrogate or placeholder for another object to control access to it. This is useful for managing operations that are resource-intensive, need to be controlled, or are complex.
Purpose
- To provide a surrogate or placeholder for another object to control access to it.
- To act as an intermediary between the client and the real object.
Use Cases
- When you need a more versatile or sophisticated reference to an object than a simple pointer.
- For lazy initialization of a heavy object.
- To control access to the original object for security reasons, to manage its lifecycle, or to add other functionalities like logging and monitoring.
Advantages/Disadvantages
Advantages:
- Can control the service object without clients knowing about it.
- Can manage the lifecycle of the service object when clients don’t care about it.
- The proxy works even if the service object isn’t ready or is not available.
Disadvantages:
- Can complicate the code due to the introduction of additional classes.
- Can introduce a slight delay to the real service object’s response time.
Implementation in JavaScript
class Subject {
request() {}
}
class RealSubject extends Subject {
request() {
return 'RealSubject: Handling request.';
}
}
class Proxy extends Subject {
constructor(realSubject) {
super();
this.realSubject = realSubject;
}
request() {
if (this.checkAccess()) {
this.realSubject.request();
this.logAccess();
}
}
checkAccess() {
// Some condition to control the access
console.log('Proxy: Checking access prior to firing a real request.');
return true;
}
logAccess() {
console.log('Proxy: Logging the time of request.');
}
}
// Usage
const realSubject = new RealSubject();
const proxy = new Proxy(realSubject);
console.log(proxy.request());
Practical Example
Consider a scenario in a backend application where we have a database service and we want to add logging and access control whenever a database query is executed.
class DatabaseService {
executeQuery(query) {
console.log(`Executing: ${query}`);
}
}
class DatabaseProxy {
constructor(service) {
this.service = service;
}
executeQuery(query) {
if (this.hasAccess()) {
this.logQuery(query);
this.service.executeQuery(query);
}
}
hasAccess() {
// Check for access rights
console.log('DatabaseProxy: Checking access rights.');
return true; // Assuming access is granted
}
logQuery(query) {
console.log(`DatabaseProxy: Logging query - ${query}`);
}
}
// Usage
const dbService = new DatabaseService();
const proxy = new DatabaseProxy(dbService);
proxy.executeQuery('SELECT * FROM users');
In this example, DatabaseProxy
acts as a proxy to DatabaseService
. It adds additional functionalities like access control and logging without changing the original database service’s code.