Chain of Responsibilty
A behavioral design pattern that lets you pass requests along a chain of handlers. Upon receiving a request, each handler decides whether to process the request or to pass it to the next handler in the chain.
It achieves loose coupling between the sender of a request and its receiver.
It enforces separation of concerns between the sender/ client and the handlers.
Use Cases
This pattern is suitable when there is a need to process different kinds of requests in a various way.
You can implement this design patterns in the following use-cases.
- Filtering requests as in an authentication process.
In this case, the request will be passed to the receiver if the validation process in each handler passes.
- Finding a suitable handler as used in the customer service workflow.
In this case, the correct handler will pick up, process, and drop the request.
Structure
Implementation
Classic Example
class Handler
{
Handler* _nextHandler;
public:
virtual Handler* SetNext(Handler *handler) = 0;
virtual std::string Handle(std::string request) = 0;
};
class DefaultAbstractHandler : public Handler {
std::string Handle(std::string request) override {
if (_nextHandler != nullptr) {
return _nextHandler->Handle(request);
}
return {};
}
Handler* SetNext(Handler* handler) override {
_nextHandler = handler;
return handler; // so you can link the next handler in a convenient way.
}
}
class SmallHandler: public DefaultAbstractHandler {
std::string Handle(std::string request) override {
if (request == "small") {
return "Small handler\n";
} else {
return DefaultAbstractHandler::Handle(request);
}
}
}
...
int main() {
SmallHandler* smallHandler = new SmallHandler;
MediumHandler* mediumHandler = new mediumHandler;
smallHandler->SetNext(mediumHandler);
std::vector<std::string> requests {"small", "medium", "large"};
for (auto& inp: requests) {
smallHandler->Handle(inp);
}
}
Improving Classic Example using std::vector
Improve further decoupling → Handlers are not aware of each other anymore
class PriceHandler {
std::vector<PriceReceiver*> _receivers;
public:
virtual PriceHandler* SetNext(PriceReceiver* handler) {
_receivers.push_back(handler);
return this;
}
virtual double HandlePrice(Ticket ticket) {
for (auto receiver: _receivers) {
double res = ...;
if (res) {
return res;
}
}
return 0;
}
virtual double HandleTotalPrice(std::vector<Ticket> tickets) {
for (Ticket ticket: tickets) {
totalPrice += HandlePrice(ticket);
}
return totalPrice;
}
}
Using Boost Event
Mentioned here .
This implementation, however, does not allow you to drop a request if it has been handled.
Image Credits
Chain of Responsibility in C++ / Design Patterns (refactoring.guru)