Open In App

Chain of Responsibility Method Design Pattern in Javascript

Last Updated : 26 Aug, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

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-Javascript
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:

cordp-
Class Diagram of Chain of Responsibility Method Design Pattern

1. 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);

Output
Level 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.



Next Article

Similar Reads