Chain Of Responsibility Design Pattern
The Chain of Responsibility Design Pattern is a behavioral pattern that allows a request to pass through a chain of handlers until one of them handles it.
Instead of having a single object responsible for processing a request, multiple objects are given a chance to handle it. The request moves along the chain until it reaches an object capable of processing it.
This design pattern promotes loose coupling between the sender and receiver. The sender does not need to know which object will handle the request — only that the request will be handled somewhere in the chain.
By decoupling request senders from receivers, we create flexible, maintainable, and efficient design.
🧩 ORM / UML Structure
The UML structure of Chain of Responsibility contains three primary participants:
- Handler (Interface / Abstract Class)
- ConcreteHandler
- Client
The key relationship is:
Client → Handler → ConcreteHandler → ConcreteHandler → ...
Each handler contains:
- A reference to the next handler
- A method to handle the request
- Logic to either process or forward the request
The power of this pattern lies in the dynamic linking of handlers at runtime.
🌟 Chain of Responsibility — Participants (for C++ Students)
Handler
- Defines the interface for handling requests.
- Stores a reference to the next Handler.
- Forwards requests if it cannot handle them.
ConcreteHandler
- Handles requests it is responsible for.
- Decides whether to process or forward.
- Passes unhandled requests to successor.
Client
- Sends requests to the first Handler.
- Does not know which Handler processes it.
- Relies on the chain for handling.
💻 C++ Code Example
Below is the example formatted cleanly for blog presentation.
Handler Interface
#ifndef HANDLER_H
#define HANDLER_H
#include <string>
class Handler {
protected:
Handler* next;
public:
Handler() : next(nullptr) {}
virtual ~Handler() {}
void setNext(Handler* handler) {
next = handler;
}
virtual void handleRequest(const std::string& request) = 0;
};
#endif
ConcreteHandlerA
#ifndef CONCRETEHANDLERA_H
#define CONCRETEHANDLERA_H
#include "Handler.h"
#include <iostream>
class ConcreteHandlerA : public Handler {
public:
void handleRequest(const std::string& request) override {
if (request == "A") {
std::cout << "ConcreteHandlerA handled request A\n";
} else if (next) {
next->handleRequest(request);
}
}
};
#endif
ConcreteHandlerB
#ifndef CONCRETEHANDLERB_H
#define CONCRETEHANDLERB_H
#include "Handler.h"
#include <iostream>
class ConcreteHandlerB : public Handler {
public:
void handleRequest(const std::string& request) override {
if (request == "B") {
std::cout << "ConcreteHandlerB handled request B\n";
} else if (next) {
next->handleRequest(request);
}
}
};
#endif
main.cpp (Client)
#include "ConcreteHandlerA.h"
#include "ConcreteHandlerB.h"
int main() {
ConcreteHandlerA handlerA;
ConcreteHandlerB handlerB;
handlerA.setNext(&handlerB);
handlerA.handleRequest("A");
handlerA.handleRequest("B");
handlerA.handleRequest("C");
return 0;
}
🧠 S.W.O.T. Analysis
✅ Strengths
- Promotes loose coupling between sender and receiver.
- Flexible and dynamic chain construction.
- Respects Open/Closed Principle.
⚠️ Weaknesses
- Can be harder to debug.
- No guarantee a request will be handled.
- Chain configuration must be done carefully.
🚀 Opportunities
- Ideal for middleware systems.
- Excellent for event processing pipelines.
- Common in logging, GUI event handling, and request processing systems.
⚡ Threats
- Long chains may reduce performance.
- Improper chain setup can cause silent failures.
- Overuse may complicate simple workflows.






