TypeScript Chain of Responsibility Pattern Example

1. Definition

The Chain of Responsibility pattern is a behavioral design pattern that decouples request senders from receivers by allowing multiple objects to handle the request. The request is passed through a chain of potential handlers until either a handler takes care of it or the end of the chain is reached.

2. Problem Statement

In scenarios where a request can be handled by multiple objects but should be processed by only one of them, directly coupling the sender to the receiver can become a problem. It leads to tight coupling and lacks flexibility in distributing the responsibilities among the handlers.

3. Solution

Create a chain of loosely coupled handler objects. A request is passed from one handler to the next in the chain until a handler processes it or the chain ends. By doing this, the system becomes more flexible and scalable.

4. Real-World Use Cases

1. Event propagation in GUI components.

2. Middleware in web frameworks where each middleware processes a request and then passes it to the next middleware.

3. Input validation systems where multiple validation checks are run sequentially.

5. Implementation Steps

1. Define a Handler interface that declares a method for handling requests and setting the next handler in the chain.

2. Create concrete handler classes that process the requests based on their own criteria.

3. Connect the handlers in a chain in the client code.

6. Implementation in TypeScript

// Step 1: Define a Handler interface
interface Handler {
    setNext(handler: Handler): Handler;
    handle(request: string): string | null;
}
// Step 2: Implement concrete handlers
class ConcreteHandlerA implements Handler {
    private nextHandler: Handler | null = null;
    setNext(handler: Handler): Handler {
        this.nextHandler = handler;
        return handler;
    }
    handle(request: string): string | null {
        if (request === 'A') {
            return `Handler A: Handling request ${request}`;
        }
        return this.nextHandler ? this.nextHandler.handle(request) : null;
    }
}
class ConcreteHandlerB implements Handler {
    private nextHandler: Handler | null = null;
    setNext(handler: Handler): Handler {
        this.nextHandler = handler;
        return handler;
    }
    handle(request: string): string | null {
        if (request === 'B') {
            return `Handler B: Handling request ${request}`;
        }
        return this.nextHandler ? this.nextHandler.handle(request) : null;
    }
}
// Client code
const handler1 = new ConcreteHandlerA();
const handler2 = new ConcreteHandlerB();
handler1.setNext(handler2);
console.log(handler1.handle('A'));
console.log(handler1.handle('B'));
console.log(handler1.handle('C'));

Output:

Handler A: Handling request A
Handler B: Handling request B
null

Explanation:

The Chain of Responsibility pattern promotes the idea of loose coupling, where each handler is only aware of the next handler in the chain. 

In the TypeScript example, requests are passed from ConcreteHandlerA to ConcreteHandlerB. If neither can handle the request, it returns null. The handlers process the request based on their criteria and pass it along if they can't handle it.

7. When to use?

The Chain of Responsibility pattern is useful when:

1. More than one object should be given a chance to process a request.

2. The set of handlers for a request should be dynamically specified.

3. You want to reduce the coupling between senders and receivers by allowing multiple objects to handle the request independently.


Comments