Chain of Responsibility Design Pattern in JavaScript

1. Definition

The Chain of Responsibility Design Pattern allows an object to pass the request along the chain of potential handlers until an object handles it or the end of the chain is reached. It decouples request senders from receivers by allowing multiple objects to handle the request independently.

2. Problem Statement

Imagine you're building a system where multiple operations or checks need to be performed in a sequence, but not all checks are necessary for every request. How can you organize these checks so that they're modular, easily maintainable, and executed in a specific order?

3. Solution

Create a chain of handler objects where each handler decides whether to process the request and pass it down the chain. Each handler in the chain will have a reference to the next handler, forming a linked list of handlers.

4. Real-World Use Cases

1. Event propagation in DOM where an event can be handled by a target element, its parent, or even its ancestors.

2. Middleware in web frameworks that process requests and responses.

3. Input validation checks in forms where different validations are executed in sequence.

5. Implementation Steps

1. Define a handler interface which specifies a method for setting the next handler and a method to execute the request.

2. Create concrete handlers that implement the handler interface. Each handler will decide whether to process the request or pass it to the next handler in the chain.

3. In the client, create the chain by linking handlers.

6. Implementation in JavaScript

// Handler interface
class Handler {
    setNext(handler) {}
    handle(request) {}
}
// Concrete handler
class HandlerA extends Handler {
    setNext(handler) {
        this.nextHandler = handler;
    }
    handle(request) {
        if (request === 'RequestA') {
            console.log("HandlerA handling RequestA.");
        } else if (this.nextHandler) {
            this.nextHandler.handle(request);
        }
    }
}
// Another concrete handler
class HandlerB extends Handler {
    setNext(handler) {
        this.nextHandler = handler;
    }
    handle(request) {
        if (request === 'RequestB') {
            console.log("HandlerB handling RequestB.");
        } else if (this.nextHandler) {
            this.nextHandler.handle(request);
        }
    }
}
// Client Code
const handlerA = new HandlerA();
const handlerB = new HandlerB();
handlerA.setNext(handlerB);
handlerA.handle('RequestA');
handlerA.handle('RequestB');

Output:

HandlerA handling RequestA.
HandlerB handling RequestB.

Explanation:

1. The Handler interface defines the structure for all concrete handlers.

2. HandlerA and HandlerB are concrete handlers that either handle the request or pass it to the next handler.

3. In the client code, we create a chain where HandlerA is followed by HandlerB. When a request is passed to HandlerA, it either handles it or delegates it to HandlerB.

4. The output shows that both requests, 'RequestA' and 'RequestB', are handled by their respective handlers.

7. When to use?

The Chain of Responsibility pattern is useful when:

1. Multiple objects can handle a request and the handler isn't known a priori, but should be determined automatically.

2. You want to issue a request to one of several objects without specifying the receiver explicitly.

3. The set of handlers can be dynamically reconfigured, or new handlers can be added.


Comments