Chain of Responsibilty

·

2 min read

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)

Did you find this article valuable?

Support Aries by becoming a sponsor. Any amount is appreciated!