Proxy Method Design Pattern in Javascript
Last Updated :
02 Sep, 2024
The Proxy Method Design Pattern in JavaScript involves creating a proxy object that controls access to another object, allowing you to define custom behavior for operations like property access and method calls. This pattern is useful for adding features such as logging, validation, and access control in a modular way. In this article, we'll explore how the Proxy pattern works in JavaScript, its benefits, and practical examples to demonstrate its application in managing and enhancing object interactions.
Proxy Method Design Pattern in JavascriptImportant Topics for Proxy Method Design Pattern in Javascript
What is the Proxy Method Design Pattern?
The Proxy Method Design Pattern involves creating a surrogate or placeholder object as a middleman between a client and a target object. It allows you to intercept operations before they reach the target object, modify or enhance them, and then pass them on. This pattern is particularly useful for controlling access, caching data, and providing a simplified interface to a complex or resource-intensive object.
Components of Proxy Method Design Pattern
The Proxy Method Design Pattern typically consists of the following components:
- Subject Interface: Defines the common interface for the real object and the proxy, ensuring that both can be used interchangeably.
- Real Subject: The actual object that the proxy represents and forwards requests to. It contains the core functionality and data.
- Proxy: Acts as an intermediary between the client and the real subject. It controls access to the real subject, adding additional functionality such as logging, access control, or lazy initialization.
- Client: The entity that interacts with the proxy and, by extension, the real subject. It uses the proxy interface to request operations.
Proxy Method Design Pattern example in Javascript
Problem Statement:
Imagine a scenario where you have an online document editor application that allows users to view and edit documents. However, accessing and editing certain documents might require permission due to their sensitivity or size. Loading large documents might be slow, and unauthorized access should be prevented. You need a mechanism to control access to these documents and optimize performance by loading them only when necessary.
How the Proxy Pattern Solves the Problem?
The Proxy Method Design Pattern can be applied here to create a proxy for the document object. The proxy will handle access control and lazy loading of the document content. This means that the document will only be loaded when the user has the necessary permissions and when they actually need to view or edit it. The proxy also helps in reducing the load on the system by deferring the document loading until it is absolutely required.
Class Diagram of Proxy Method Design Pattern in JavascriptBelow is the code for the approach discussed above:
1. Subject Interface
Defines the operations that both the real document and the proxy will implement.
JavaScript
// Subject Interface
class Document {
open() {}
edit(content) {}
}
2. Real Subject
The actual document object that contains the real content and the methods to interact with it.
JavaScript
// Real Subject
class RealDocument extends Document {
constructor(filename) {
super();
this.filename = filename;
this.content = this.loadFromDisk();
}
loadFromDisk() {
console.log(`Loading document from disk: ${this.filename}`);
return "Document content..."; // Simulate loading content
}
open() {
console.log(`Opening document: ${this.filename}`);
return this.content;
}
edit(newContent) {
console.log(`Editing document: ${this.filename}`);
this.content = newContent;
}
}
3. Proxy
The proxy object that controls access to the real document. It handles permission checks and lazy loading of the document content.
JavaScript
// Proxy
class DocumentProxy extends Document {
constructor(filename, user) {
super();
this.filename = filename;
this.user = user;
this.realDocument = null;
}
checkAccess() {
console.log(`Checking access for user: ${this.user}`);
return this.user === "admin"; // Example permission check
}
open() {
if (this.checkAccess()) {
if (!this.realDocument) {
this.realDocument = new RealDocument(this.filename);
}
return this.realDocument.open();
} else {
console.log("Access Denied: Unauthorized user");
return null;
}
}
edit(newContent) {
if (this.checkAccess()) {
if (!this.realDocument) {
this.realDocument = new RealDocument(this.filename);
}
this.realDocument.edit(newContent);
} else {
console.log("Access Denied: Unauthorized user");
}
}
}
4. Client
The client interacts with the proxy, unaware of whether it is dealing with a proxy or the real document.
JavaScript
// Client
const user = "admin";
const docProxy = new DocumentProxy("sample.txt", user);
// Open document
console.log(docProxy.open());
// Edit document
docProxy.edit("New content...");
// Open again to see the changes
console.log(docProxy.open());
Below is the complete combined code:
JavaScript
// Subject Interface
class Document {
open() {}
edit(content) {}
}
// Real Subject
class RealDocument extends Document {
constructor(filename) {
super();
this.filename = filename;
this.content = this.loadFromDisk();
}
loadFromDisk() {
console.log(`Loading document from disk: ${this.filename}`);
return "Document content..."; // Simulate loading content
}
open() {
console.log(`Opening document: ${this.filename}`);
return this.content;
}
edit(newContent) {
console.log(`Editing document: ${this.filename}`);
this.content = newContent;
}
}
// Proxy
class DocumentProxy extends Document {
constructor(filename, user) {
super();
this.filename = filename;
this.user = user;
this.realDocument = null;
}
checkAccess() {
console.log(`Checking access for user: ${this.user}`);
return this.user === "admin"; // Example permission check
}
open() {
if (this.checkAccess()) {
if (!this.realDocument) {
this.realDocument = new RealDocument(this.filename);
}
return this.realDocument.open();
} else {
console.log("Access Denied: Unauthorized user");
return null;
}
}
edit(newContent) {
if (this.checkAccess()) {
if (!this.realDocument) {
this.realDocument = new RealDocument(this.filename);
}
this.realDocument.edit(newContent);
} else {
console.log("Access Denied: Unauthorized user");
}
}
}
// Client
const user = "admin";
const docProxy = new DocumentProxy("sample.txt", user);
// Open document
console.log(docProxy.open());
// Edit document
docProxy.edit("New content...");
// Open again to see the changes
console.log(docProxy.open());
Below is the explanation of the above code:
- RealDocument: Represents the actual document with methods to open and edit the document. The content is loaded from the disk when the document is created.
- DocumentProxy: Acts as a mediator between the client and the RealDocument. It ensures that only authorized users can access the document and defers the loading of the document until it is necessary.
- Client: Interacts with the DocumentProxy, which controls access to the RealDocument. The client does not need to worry about the underlying complexities of permission checking or document loading.
Use Cases of Proxy Method Design Pattern
- Access Control: Proxy can control access to an object by checking permissions or authentication.
- Caching: Proxy can cache results of expensive operations and serve cached data instead of recalculating.
- Logging: Proxy can add logging capabilities before and after invoking methods on the RealSubject.
- Lazy Initialization: Proxy can delay the creation and initialization of the RealSubject until it's actually needed.
When to Use Proxy Method Design Pattern
- Lazy Initialization (Virtual Proxy): When you want to delay the creation and initialization of expensive objects until they are actually needed. The proxy acts as a placeholder, creating the real object only when required.
- Access Control (Protection Proxy): When you need to control access to an object, especially when different users or clients have different levels of access. The proxy can manage the access rights and enforce security rules before delegating requests to the actual object.
- Remote Proxy: When the actual object exists in a different address space (e.g., on a different server), the proxy provides a local representative for the remote object, handling communication between the client and the remote object.
- Logging or Monitoring: When you need to monitor or log operations without modifying the original object. The proxy can intercept requests, log them, and then pass them to the real object.
- Smart Reference: When you need to add additional functionality when accessing an object, such as reference counting, caching, or lazy loading. The proxy can manage these additional responsibilities while still delegating core functionality to the real object.
When Not to Use Proxy Method Design Pattern
The Proxy Method Design Pattern might not be the best choice in the following situations:
- Overhead Concerns: If performance is critical and the overhead of using a proxy (additional method calls, added layers of indirection) would significantly impact the system's efficiency, it's better to avoid this pattern.
- Simple Objects: When dealing with simple objects that don't require additional layers of control, access, or initialization, using a proxy can unnecessarily complicate the design.
- Direct Access Required: If clients need to interact directly with the actual object without any intermediary, perhaps due to strict performance requirements or system constraints, a proxy pattern may not be appropriate.
- High Complexity: Introducing a proxy can add complexity to the system. If the system is already complex, adding a proxy might make it harder to maintain or understand, especially if the benefits of the proxy are marginal.
- Real-Time Systems: In real-time systems where every millisecond counts, the additional delay introduced by a proxy can be unacceptable. Direct interaction with objects may be required to meet strict timing constraints.
Conclusion
The Proxy Method Design Pattern in JavaScript offers a flexible way to add behavior to objects indirectly. By providing a surrogate or placeholder, it enhances control over object access, caching, and additional functionality while maintaining a consistent interface with the client. By leveraging proxies, developers can achieve better separation of concerns, improved code reusability, and enhanced security in their applications
Similar Reads
Proxy Method Design Pattern in Java
A Proxy Method or Proxy Design Pattern is a structural design pattern that provides a surrogate or placeholder for another object to control access to it. This pattern involves creating a new class, known as the proxy, which acts as an intermediary between a client and the real object. The proxy obj
9 min read
Prototype Method - JavaScript Design Pattern
A Prototype Method is a JavaScript design pattern where objects share a common prototype object, which contains shared methods. The prototype method allows you to reuse the properties and methods of the prototype object, and also add new ones as needed. The prototype method is useful for performance
3 min read
Singleton Method Design Pattern in JavaScript
Singleton Method or Singleton Design Pattern is a part of the Gang of Four design pattern and it is categorized under creational design patterns. It is one of the most simple design patterns in terms of modeling but on the other hand, this is one of the most controversial patterns in terms of comple
10 min read
Strategy Method | JavaScript Design Pattern
Strategy Method or Strategy Pattern in JavaScript helps solve the problem of needing to use different methods or behaviors in your code and easily switch between them. Strategy Method is a behavioral design pattern in JavaScript that defines a family of algorithms, encapsulates each one, and makes t
8 min read
Template Method | JavaScript Design Patterns
Template Method is a behavioral design pattern that defines the skeleton of an algorithm in a base class while allowing subclasses to implement specific steps of the algorithm without changing its structure. It promotes code reusability and provides a way to enforce a consistent algorithm structure
10 min read
Bridge Method | JavaScript Design Pattern
In software design, as systems grow and evolve, the complexity of their components can increase exponentially. This complexity often arises from the intertwining of different functionalities and features, making the system rigid, less maintainable, and harder to scale. The Bridge design pattern emer
9 min read
Interpreter Method Design Pattern in Javascript
The Interpreter pattern is a behavioral design pattern that defines a grammatical representation of a language and provides an interpreter to process this grammar. It's commonly used to interpret expressions in a language, such as mathematical expressions, boolean expressions, or even domain-specifi
7 min read
Builder Method | JavaScript Design Pattern
The Builder design pattern is a creational design pattern used to construct complex objects by separating the construction process from the actual representation. It's especially useful when an object requires multiple steps or configurations to be created. Important Topics for the Builder Design Pa
9 min read
Chain of Responsibility Method Design Pattern in Javascript
The Chain of Responsibility design pattern in JavaScript allows a request to be passed through a chain of handlers until one processes it. This pattern helps manage complex workflows by decoupling the sender of a request from its receivers, making the system more flexible and easier to extend. In th
6 min read
Mediator Design Pattern in JavaScript | Design Pattern
The Mediator pattern is a behavioral design pattern that promotes loose coupling between objects by centralizing communication between them. It's particularly useful when you have a complex system with multiple objects that need to interact and you want to avoid the tight coupling that can arise fro
5 min read