Chain of Responsibility Method Design Pattern in Javascript
Last Updated : 26 Aug, 2024
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 this article, we'll explore how to implement the Chain of Responsibility in JavaScript, with practical examples to demonstrate its use in creating more maintainable and scalable code.
Chain of Responsibility Method Design Pattern in JavascriptImportant Topics for Chain of Responsibility Method Design Pattern in Javascript
What is the Chain of Responsibility Method Design Pattern?
The Chain of Responsibility method design pattern is a behavioral pattern that allows multiple objects to handle a request, passing it along a chain until one of the objects handles it. Each object in the chain either processes the request or passes it to the next handler in the chain. This pattern is useful for decoupling the sender of a request from its receivers, providing a way to handle requests by different handlers without hard-wiring the request-processing code to specific classes. It promotes flexibility in assigning responsibilities to objects dynamically.
Components of Chain of Responsibility Method Design Pattern
The Chain of Responsibility design pattern consists of several key components:
- Handler Interface: Defines an interface for handling requests. It typically includes a method to handle the request and a method to set the next handler in the chain.
- Concrete Handler: Implements the handler interface and processes the request. If the handler cannot process the request, it passes it to the next handler in the chain.
- Client: Initiates the request and sets up the chain of handlers. The client sends the request to the first handler in the chain.
- Next Handler Reference: A reference in each handler to the next handler in the chain, allowing the request to be passed along the chain if the current handler cannot process it.
Chain of Responsibility Method Design Pattern Example in Javascript
Problem Statement:
Imagine a tech support system where customer queries are handled by different levels of support agents. A basic query can be handled by a Level 1 support agent, but more complex issues need to be escalated to Level 2 or even Level 3 support. Without a clear process, queries might be mishandled, leading to delays or unresolved issues. The challenge is to efficiently route each customer query to the appropriate support level.
How does the Chain of Responsibility Method Design Pattern solve this problem?
The Chain of Responsibility design pattern can be applied to solve this problem. Each support level will be a handler in the chain, processing the query or passing it on to the next level if it cannot handle it.
Below is the implementation of the above approach:
Class Diagram of Chain of Responsibility Method Design Pattern1. Handler Interface
JavaScript class SupportHandler { setNextHandler(handler) { this.nextHandler = handler; } handleRequest(query) { throw new Error("This method should be overridden!"); } }
2. Concrete Handlers (Level 1, Level 2, and Level 3 Support)
JavaScript class Level1Support extends SupportHandler { handleRequest(query) { if (query.difficulty === "easy") { console.log("Level 1 Support: Handling easy query."); } else if (this.nextHandler) { this.nextHandler.handleRequest(query); } } } class Level2Support extends SupportHandler { handleRequest(query) { if (query.difficulty === "medium") { console.log("Level 2 Support: Handling medium query."); } else if (this.nextHandler) { this.nextHandler.handleRequest(query); } } } class Level3Support extends SupportHandler { handleRequest(query) { if (query.difficulty === "hard") { console.log("Level 3 Support: Handling hard query."); } else if (this.nextHandler) { this.nextHandler.handleRequest(query); } else { console.log("Query cannot be handled at any level."); } } }
3. Client Setup
JavaScript // Creating support levels const level1 = new Level1Support(); const level2 = new Level2Support(); const level3 = new Level3Support(); // Setting up the chain level1.setNextHandler(level2); level2.setNextHandler(level3); // Example queries const easyQuery = { difficulty: "easy" }; const mediumQuery = { difficulty: "medium" }; const hardQuery = { difficulty: "hard" }; const unknownQuery = { difficulty: "unknown" }; // Handling the queries level1.handleRequest(easyQuery); level1.handleRequest(mediumQuery); level1.handleRequest(hardQuery); level1.handleRequest(unknownQuery);
Below is the complete code provided:
JavaScript // Handler Interface class SupportHandler { setNextHandler(handler) { this.nextHandler = handler; } handleRequest(query) { throw new Error("This method should be overridden!"); } } // Concrete Handlers class Level1Support extends SupportHandler { handleRequest(query) { if (query.difficulty === "easy") { console.log("Level 1 Support: Handling easy query."); } else if (this.nextHandler) { this.nextHandler.handleRequest(query); } } } class Level2Support extends SupportHandler { handleRequest(query) { if (query.difficulty === "medium") { console.log("Level 2 Support: Handling medium query."); } else if (this.nextHandler) { this.nextHandler.handleRequest(query); } } } class Level3Support extends SupportHandler { handleRequest(query) { if (query.difficulty === "hard") { console.log("Level 3 Support: Handling hard query."); } else if (this.nextHandler) { this.nextHandler.handleRequest(query); } else { console.log("Query cannot be handled at any level."); } } } // Client Code const level1 = new Level1Support(); const level2 = new Level2Support(); const level3 = new Level3Support(); level1.setNextHandler(level2); level2.setNextHandler(level3); const easyQuery = { difficulty: "easy" }; const mediumQuery = { difficulty: "medium" }; const hardQuery = { difficulty: "hard" }; const unknownQuery = { difficulty: "unknown" }; level1.handleRequest(easyQuery); level1.handleRequest(mediumQuery); level1.handleRequest(hardQuery); level1.handleRequest(unknownQuery);
OutputLevel 1 Support: Handling easy query. Level 2 Support: Handling medium query. Level 3 Support: Handling hard query. Query cannot be handled at any level.
Below is the explanation of the above code:
- SupportHandler: This is the base class with methods to set the next handler and handle requests.
- Level1Support, Level2Support, Level3Support: These are concrete handlers. Each one checks if it can handle the query. If not, it passes the query to the next handler in the chain.
- Client Setup: The client sets up the chain of responsibility by linking the support levels. When a query is passed to the first handler, it either processes it or passes it along the chain.
This design pattern is effective in handling queries of varying complexity by delegating responsibility through a chain, ensuring each query reaches the appropriate handler.
When to Use the Chain of Responsibility Design Pattern
- When multiple objects can handle a request and the handler is determined at runtime.
- When you want to decouple the sender and receiver of a request.
- When you want to simplify the code by allowing request processing to be passed through a chain of handlers.
When Not to Use the Chain of Responsibility Design Pattern
- When a single handler should always handle a request.
- When the request processing should not be passed to multiple handlers.
- When performance is a concern, as passing requests through a chain can introduce overhead.
By implementing the Chain of Responsibility pattern, you can create flexible and decoupled systems where requests are handled by different handlers based on their type or context. This pattern is especially useful in scenarios where you have a series of processing steps that can be organized into a pipeline of handlers.