Skip to main content

Introduction To The Chain of Responsibility Pattern

The Chain of Responsibility is a behavioral design pattern that lets you pass requests along a chain of handlers. Upon receiving a request, each handler decides either to process the request or to pass it to the next handler in the chain.

In this diagram:

  • Handler is an interface with two methods setNext and handle.
  • AbstractHandler is an abstract class that implements Handler. It has a private member nextHandler and provides an implementation for the setNext and handle methods.
  • MonkeyHandler, SquirrelHandler, and DogHandler are classes that extend AbstractHandler and provide their own implementation of the handle method.

The pattern is typically used when a set of objects should be able to handle a request, but the specific handler isn't known a priori; it can be determined runtime. The client (request originator) sends the request to the first handler in the chain. If the handler can't handle it, it forwards the request to the next handler in the chain, and so on.

Classic Implementation

Here is a step by step example of how you might implement a Chain of Responsibility in TypeScript:

// Handler interface with updated handle method to return string | null.
interface Handler {
setNext(handler: Handler): Handler;

handle(request: string): string | null;
}

// Abstract handler with handle method implementation updated.
abstract class AbstractHandler implements Handler {
private nextHandler: Handler | null = null;

public setNext(handler: Handler): Handler {
this.nextHandler = handler;
// Returning a handler from setNext will allow
// us to link handlers in a convenient way
// like this: monkey.setNext(squirrel).setNext(dog);
return handler;
}

public handle(request: string): string | null {
if (this.nextHandler) {
return this.nextHandler.handle(request);
}
return null;
}
}

class MonkeyHandler extends AbstractHandler {
public handle(request: string): string | null {
if (request === "Banana") {
return `Monkey: I'll eat the ${request}.`;
}
return super.handle(request);
}
}

class SquirrelHandler extends AbstractHandler {
public handle(request: string): string | null {
if (request === "Nut") {
return `Squirrel: I'll eat the ${request}.`;
}
return super.handle(request);
}
}

class DogHandler extends AbstractHandler {
public handle(request: string): string | null {
if (request === "MeatBall") {
return `Dog: I'll eat the ${request}.`;
}
return super.handle(request);
}
}

// Client Code
function clientCode(handler: Handler) {
const foods = ["Nut", "Banana", "Cup of coffee", "MeatBall"];

for (const food of foods) {
console.log(`Who wants a ${food}?`);

const result = handler.handle(food);
if (result) {
console.log(`${result}`);
} else {
console.log(`${food} was left untouched.`);
}
}
}
// Create individual handlers.
const monkey = new MonkeyHandler();
const squirrel = new SquirrelHandler();
const dog = new DogHandler();

// Link the handlers in the chain: monkey -> squirrel -> dog
monkey.setNext(squirrel).setNext(dog);

// Use the client code to process the array of foods starting with the monkey handler.
clientCode(monkey);

This code first creates a chain of three handlers (MonkeyHandler, Squirrel Handler, DogHandler), and then makes several requests that get passed along that chain. Each handler on the chain will either process the request (if it can) or pass it to the next handler. The first handler that can process the request does so, and no other handlers are involved.

When To Use Chain Of Responsibilty

The Chain of Responsibility pattern is generally applicable when you have multiple objects that can potentially handle a request and their exact handler isn't predetermined but is determined at runtime.

Here are some signs, or "code smells", that may suggest the Chain of Responsibility pattern could be a useful approach:

Coupling

You see that the object which sends the request needs to know too much detail about who handles the request, how it is handled, and the sequence of handling. This high coupling is not good for the maintainability and scalability of the code.

Multiple Conditionals

Your code has multiple conditionals (like if/else or switch statements) to determine how to process a certain request. This is sometimes referred to as "Conditional Complexity" and using the Chain of Responsibility pattern can help distribute these conditionals across different classes, each handling its own logic.

Varying Processing Logic

The processing logic varies often. This could mean that new handlers need to be added or existing ones need to be removed frequently. Chain of Responsibility allows you to do this easily without changing the client code.

Uncertain Processing Path

The path of processing isn't linear and may change dynamically based on factors that can only be determined at runtime. Chain of Responsibility pattern allows for dynamic determination of the processing path.

Code Duplication

You notice that similar pieces of code are scattered in different parts of your codebase and each piece is doing a part of the processing. This code smell might be an indication that you can bring all these separate pieces under one roof through a chain of responsibility.

Sequential Processing Required

When a task should be processed sequentially by multiple entities in a specific order, the Chain of Responsibility is a suitable pattern. The order can be changed easily without modifying the individual handler's code.

While these "code smells" may suggest that Chain of Responsibility could be applicable, they don't necessarily mean it is the best or only solution. Other design patterns could also be appropriate depending on the specific context. Use your best judgement when deciding to apply a design pattern.

TypeScript Course Instructor Image
TypeScript Course Instructor Image

Time To Transition From JavaScript To TypeScript

Level Up Your TypeScript And Object Oriented Programming Skills. The only complete TypeScript course on the marketplace you building TypeScript apps like a PRO.

SEE COURSE DETAILS

What Can You Do Next 🙏😊

If you liked the article, consider subscribing to Cloudaffle, my YouTube Channel, where I keep posting in-depth tutorials and all edutainment stuff for software developers.

YouTube @cloudaffle