Introduction

Imagine you are building a customer support system.

Whenever a customer raises a ticket, the ticket may need to be handled by different support levels:

  • Level 1 Support

  • Level 2 Support

  • Manager

  • Director

A simple issue might be resolved by Level 1 Support.

A technical issue might be forwarded to Level 2 Support.

A business-critical issue may eventually reach the Director.

Now imagine writing code like this:


This works initially.

But as the organization grows:

  • More handlers are added

  • Request types increase

  • Rules become complex

The code becomes difficult to maintain.

The Chain of Responsibility Pattern provides a much cleaner solution.

What is the Chain of Responsibility Pattern?

The Chain of Responsibility Pattern is a behavioral design pattern that allows a request to pass through a chain of handlers until one of them handles it.

In simple words:

A request travels through multiple handlers until someone takes responsibility for processing it.

The sender does not need to know which object will handle the request.

Real World Analogy

Imagine you visit a bank.

You need approval for a loan.

The request moves through different people:

Clerk
   ↓
Senior Officer
   ↓
Branch Manager
   ↓
Regional Manager

Each person checks:

"Can I handle this request?"

If yes:

Process Request

If no:

Forward To Next Person

This is exactly how the Chain of Responsibility Pattern works.

Why Do We Need the Chain of Responsibility Pattern?

Consider an expense approval system.

Approval rules:

Amount <= 10,000 → Team Lead

Amount <= 50,000 → Manager

Amount <= 2,00,000 → Director

Amount > 2,00,000 → CEO

Without Chain of Responsibility:


This creates several problems.

Problems Without Chain of Responsibility

1. Tight Coupling

The client knows every handler.

Client
  ├── Team Lead
  ├── Manager
  ├── Director
  └── CEO

2. Difficult to Extend

Adding a new approval level requires modifying existing code.

3. Violates the Open-Closed Principle

Every new handler requires changing the if-else logic.

4. Complex Conditional Statements

As rules grow, conditions become harder to maintain.

Solution: Chain of Responsibility Pattern

The Chain of Responsibility Pattern creates a chain of handlers.

Instead of:

Client decides handler

We do:

Client
   ↓
Handler 1
   ↓
Handler 2
   ↓
Handler 3
   ↓
Handler 4

Each handler decides:

Can I handle this request?

If yes:

Handle Request

If no:

Forward To Next Handler

Key Components of the Chain of Responsibility Pattern

The pattern usually consists of:

1. Handler Interface

Defines request handling behavior.

2. Concrete Handlers

Actual implementations that process requests.

3. Chain

Links handlers together.

4. Client

Sends requests into the chain.

Structure of the Chain of Responsibility Pattern

Request
   │
   ▼
Handler A
   │
   ▼
Handler B
   │
   ▼
Handler C
   │
   ▼
Handler D

The request keeps moving until a handler processes it.

Example: Expense Approval System

Let's implement an expense approval workflow.

Step 1: Create Request Class


Step 2: Create Handler Interface


Step 3: Create Team Lead Handler


Step 4: Create Manager Handler


Step 5: Create Director Handler


Step 6: Create CEO Handler


Step 7: Create the Chain


Output

Approved by Director

How the Chain Works

Let's follow the request flow.

Request:

ExpenseRequest(75000)

First handler:

Team Lead

Check:

75000 <= 10000 ?

No.

Forward.

Next:

Manager

Check:

75000 <= 50000 ?

No.

Forward.

Next:

Director

Check:

75000 <= 200000 ?

Yes.

The request is handled.

Key Idea Behind the Pattern

The most important idea is:

Give multiple objects an opportunity to handle a request without the sender knowing which object will handle it.

The client simply submits the request.

The chain decides where it should go.

Request Can Be Handled by Multiple Handlers

Sometimes every handler processes the request.

Example:

Authentication Middleware
      ↓
Logging Middleware
      ↓
Authorization Middleware
      ↓
Business Logic

Each handler performs some work before passing the request forward.

This is very common in web frameworks.

Chain of Responsibility vs Strategy Pattern

Chain of ResponsibilityStrategy
Multiple handlers existOne strategy is selected
Request travels through the chainThe algorithm executes directly
The handler decides whether to processClient selects behavior
Focuses on request routingFocuses on algorithm selection

Chain of Responsibility vs Command Pattern

Chain of ResponsibilityCommand
Decides who handles the requestEncapsulates the request as an object
Multiple possible handlersOne receiver usually executes
Request forwarding is importantExecution is important

Advantages of Chain of Responsibility

1. Loose Coupling

The sender does not know the receiver.

2. Easy Extensibility

New handlers can be added easily.

3. Supports the Open-Closed Principle

New behavior can be added without modifying existing handlers.

4. Cleaner Code

Eliminates large conditional statements.

5. Flexible Request Routing

Requests can be dynamically routed through different chains.

Disadvantages of the Chain of Responsibility

1. Request May Go Unhandled

If no handler processes the request, it may be lost.

Example:

Handler A
   ↓
Handler B
   ↓
End of Chain

No handler accepts the request.

2. Difficult Debugging

Tracing the request path can become difficult in large systems.

3. Performance Overhead

Very long chains can increase processing time.

Real World Applications

The Chain of Responsibility Pattern is widely used in:

  • Customer support escalation systems

  • Expense approval workflows

  • Banking approval systems

  • Middleware frameworks

  • Authentication pipelines

  • Logging frameworks

  • Event processing systems

  • Web request processing

  • Exception handling frameworks

Many modern web frameworks internally implement middleware using this pattern.

Common Beginner Mistakes

1. Forgetting to Forward Requests

A handler should forward the request when it cannot process it.

Example:

nextApprover->approve(request);

2. Creating Circular Chains

Avoid:

A → B → C → A

This creates infinite loops.

3. Putting All Logic in One Handler

The responsibility should be distributed across handlers.

4. Making the Client Aware of All Handlers

The client should only interact with the start of the chain.

Simple Visualization

Without Chain of Responsibility:

Client

 ├── Team Lead
 ├── Manager
 ├── Director
 └── CEO

The client knows everyone.

With Chain of Responsibility:

Client
   │
   ▼
Team Lead
   │
   ▼
Manager
   │
   ▼
Director
   │
   ▼
CEO

The client only knows the first handler.

Summary

The Chain of Responsibility Pattern allows a request to pass through a sequence of handlers until one of them processes it.

Instead of tightly coupling the sender to a specific receiver, the request flows through a chain where each handler decides whether to handle it or forward it further. This creates more flexible, extensible, and maintainable systems while eliminating large conditional statements.

Whenever multiple objects may be capable of handling a request, and you want to avoid hardcoding the receiver, the Chain of Responsibility Pattern provides a clean and scalable solution.