.. Copyright (C) Dave Parillo. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with Invariant Sections being Forward, and Preface, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License". .. index:: pair: design pattern; chain of responsibility Chain of Responsibility pattern =============================== Sometimes we need to give more than one object a chance to handle a request. The Chain of Responsibility pattern is a 'behavioral' software design pattern. The goal of the pattern is to separate request senders and request handlers, while giving more than one object a chance to handle the request. The Chain of Responsibility design pattern allows an object to send a command without knowing what object will receive and handle it. The request is sent from one object to another making them parts of a chain and each object in this chain can handle the command, pass it on or do both. The most usual example of a machine using the Chain of Responsibility is the vending machine coin slot: rather than having a slot for each type of coin, the machine has only one slot for all of them. The dropped coin is routed to the appropriate storage place that is determined by the receiver of the command. Instead of calling a single function to satisfy a request, multiple functions in the chain have a chance to satisfy the request. Since the chain is effectively a list, it can be dynamically created, so you could also think of it as a more general, dynamically-built switch statement. .. mermaid:: :alt: Food handler chain UML class diagram :caption: Food handler chain of responsibility classDiagram direction TB client ..> food_handlers : uses food_handlers --|> food_handler food_handler <|-- monkey_food_handler food_handler <|-- squirrel_food_handler food_handler <|-- dog_food_handler food_handler --* food_handlers : chain class food_handler { <> +handle(request string_view) string } class monkey_food_handler { +handle(request string_view) string } class squirrel_food_handler { +handle(request string_view) string } class dog_food_handler { +handle(request string_view) string } class food_handlers { -chain_ vector~unique_ptr~food_handler~~ +handle(food_item string_view) string } class client { <> } .. tb-group:: :name: cor_pattern_refactor_tab .. tb-tab:: Interface The interface declares methods for building the chain of handlers and for executing a request. .. code-block:: cpp struct food_handler { virtual std::string handle(std::string_view request) const = 0; virtual ~food_handler() = default; }; Note this class has no logic to visit the next node. This class has one job - provide an interface to handle a request. .. tb-tab:: Implementing classes The implementing classes are where 'handling' gets done. Classes return an empty string if they do not handle anything. The string is used as an early exit condition from the chain: as soon as some link in the change returns a non-empty string the chain can return the result to the client. .. code-block:: cpp struct monkey_food_handler : food_handler { std::string handle(std::string_view request) const override { if (request == "Banana") { return "Monkey: I'll eat the " + std::string{request} + ".\n"; } return {}; } }; struct squirrel_food_handler : food_handler { std::string handle(std::string_view request) const override { if (request == "Nut") { return "Squirrel: I'll eat the " + std::string{request} + ".\n"; } return {}; } }; struct dog_food_handler : food_handler { std::string handle(std::string_view request) const override { if (request == "MeatBall") { return "Dog: I'll eat the " + std::string{request} + ".\n"; } return {}; } }; In this example we use a vector to manage the handlers. Any iterable standard library container could be used here, but :container:`vector`, :container:`array`, and :container:`list` would be typical choices. The constructor builds the chain, which is completely private. .. code-block:: cpp class food_handlers : public food_handler { public: food_handlers() { chain_.push_back(std::make_unique()); chain_.push_back(std::make_unique()); chain_.push_back(std::make_unique()); } std::string handle(std::string_view food_item) const override { for (const auto& link: chain_) { if (std::string reply = link->handle(food_item); !reply.empty()) { return reply; } } return {}; } private: std::vector> chain_; }; No other code needs to know what classes are actually in the chain. .. tb-tab:: Client The client function provides the data that needs to be handled - in this case our food items. The client also needs to know about food handlers as a group, but does not know what classes are doing the handling or how many there are. .. code-block:: cpp void client(const food_handlers& eaters) { const std::vector food {"Nut", "Banana", "Cup of coffee"}; for (const auto& snack : food) { std::cout << "Client: Who wants a " << snack << "?\n"; const std::string result = eaters.handle(snack); if (result.empty()) { std::cout << ' ' << snack << " was left untouched.\n"; } else { std::cout << ' ' << result; } } } One of the big benefits of all this work is how little main needs to know: .. code-block:: cpp int main() { food_handlers eaters; client(eaters); } .. tb-tab:: Run It .. tb-code:: cpp :name: ac_class_design_pattern_chain_of_responsibility_refactor #include #include #include #include #include struct food_handler { virtual std::string handle(std::string_view request) const = 0; virtual ~food_handler() = default; }; struct monkey_food_handler : food_handler { std::string handle(std::string_view request) const override { if (request == "Banana") { return "Monkey: I'll eat the " + std::string{request} + ".\n"; } return {}; } }; struct squirrel_food_handler : food_handler { std::string handle(std::string_view request) const override { if (request == "Nut") { return "Squirrel: I'll eat the " + std::string{request} + ".\n"; } return {}; } }; struct dog_food_handler : food_handler { std::string handle(std::string_view request) const override { if (request == "MeatBall") { return "Dog: I'll eat the " + std::string{request} + ".\n"; } return {}; } }; class food_handlers : public food_handler { public: food_handlers() { chain_.push_back(std::make_unique()); chain_.push_back(std::make_unique()); chain_.push_back(std::make_unique()); } std::string handle(std::string_view food_item) const override { for (const auto& link: chain_) { if (std::string reply = link->handle(food_item); !reply.empty()) { return reply; } } return {}; } private: std::vector> chain_; }; void client(const food_handlers& eaters) { const std::vector food {"Nut", "Banana", "Cup of coffee"}; for (const auto& item : food) { std::cout << "Client: Who wants a " << item << "?\n"; const std::string result = eaters.handle(item); if (result.empty()) { std::cout << ' ' << item << " was left untouched.\n"; } else { std::cout << ' ' << result; } } } int main() { food_handlers eaters; client(eaters); } This next fun example is adapted from Thinking in C++, Vol 2. It uses some non-standard vocabulary to define the basic elements of the chain, but it is still a chain of responsibility. .. tb-group:: :name: cor_pattern_ticpp_tab .. tb-tab:: Interface First we define an interface each handler in the chain of responsibility must implement. .. code-block:: cpp #include #include #include enum class Answer { NO, YES }; // This is our handler interface. // Every class that inherits from this // must implement the canIHave function struct GimmeStrategy { virtual Answer canIHave() = 0; virtual ~GimmeStrategy() = default; }; Rather than a ``bool``, in this case, our early termination criteria is an enumerated type. .. tb-tab:: Implementing classes For a chain to be a chain, at least two classes must implement the interface. (It's not much of a chain with only 1 link). .. code-block:: cpp struct AskMom : public GimmeStrategy { Answer canIHave() { std::cout << "Mommy? Can I have this?\n"; return Answer::NO; } }; struct AskDad : public GimmeStrategy { Answer canIHave() { std::cout << "Dad, I really need this!\n"; return Answer::NO; } }; struct AskGrandpa : public GimmeStrategy { Answer canIHave() { std::cout << "Grandpa, is it my birthday yet?\n"; return Answer::NO; } }; struct AskGrandma : public GimmeStrategy { Answer canIHave() { std::cout << "Grandma, I really love you!\n"; return Answer::YES; } }; .. tb-tab:: Building the chain Much discussion related to this pattern is about how to create the chain of responsibility as a linked list. However, when you look at the pattern it really shouldn't matter how the chain is created: that's an implementation detail. The only important part is that *some* kind of :term:`iterable` type is used to visit each handler. How that is implemented should be invisible to users. While the ``Gimme`` class also is derived from the ``GimmeStrategy`` it is used to construct the chain of all the other strategies used. .. code-block:: cpp class Gimme : public GimmeStrategy { private: std::vector> chain; public: Gimme() { chain.push_back(std::unique_ptr(new AskMom)); chain.push_back(std::unique_ptr(new AskDad)); chain.push_back(std::unique_ptr(new AskGrandpa)); chain.push_back(std::unique_ptr(new AskGrandma)); } Answer canIHave() { for (const auto& it: chain) { if (it->canIHave() == Answer::YES) { return Answer::YES; } } // Reached end without success... std::cout << "Waaaaaahh!!\n"; return Answer::NO; } }; .. tb-tab:: Run It Once the abstract and implementing classes have been defined, then calling the chain is easy: .. code-block:: cpp int main() { Gimme chain; chain.canIHave(); } .. tb-code:: cpp :name: ac_class_design_pattern_chain_of_responsibility_ticpp #include #include #include enum class Answer { NO, YES }; struct GimmeStrategy { virtual Answer canIHave() = 0; virtual ~GimmeStrategy() = default; }; struct AskMom : public GimmeStrategy { Answer canIHave() { std::cout << "Mommy? Can I have this?\n"; return Answer::NO; } }; struct AskDad : public GimmeStrategy { Answer canIHave() { std::cout << "Dad, I really need this!\n"; return Answer::NO; } }; struct AskGrandpa : public GimmeStrategy { Answer canIHave() { std::cout << "Grandpa, is it my birthday yet?\n"; return Answer::NO; } }; struct AskGrandma : public GimmeStrategy { Answer canIHave() { std::cout << "Grandma, I really love you!\n"; return Answer::YES; } }; class Gimme : public GimmeStrategy { private: std::vector> chain; public: Gimme() { chain.push_back(std::unique_ptr(new AskMom)); chain.push_back(std::unique_ptr(new AskDad)); chain.push_back(std::unique_ptr(new AskGrandpa)); chain.push_back(std::unique_ptr(new AskGrandma)); } Answer canIHave() { for (const auto& it: chain) { if (it->canIHave() == Answer::YES) { return Answer::YES; } } // Reached end without success... std::cout << "Waaaaaahh!!\n"; return Answer::NO; } }; int main() { Gimme chain; chain.canIHave(); } The 'classic' version --------------------- The 'classic' implementation of the chain of responsibility implements the design shown in the UML diagram with a handler interface and an abstract handler to encapsulate maintenance of the chain. .. mermaid:: :alt: chain of responsibility UML diagram classDiagram client ..> handler handler --> handler : next handler <|-- receiver1 handler <|-- receiver2 handler <|-- receiver3 class handler { <> +handle() } class receiver1 { +handle() override } class receiver2 { +handle() override } class receiver3 { +handle() override } The 'classic' Chain of Responsibility in the 'Gang of Four' Patterns book does some work that is not needed in C++. Specifically the code related to managing a linked list from scratch. The standard library provides several containers that will work just as well. This is one of the reasons why this pattern is no longer implemented as written in the original book. Keeping in mind that the essence of Chain of Responsibility is to try a number of solutions until you find one that works, you'll realize that the implementation of the sequencing mechanism is not an essential part of the pattern. .. tb-group:: :name: cor_pattern_cppguru_tab .. tb-tab:: Interface The Handler interface declares methods for building the chain of handlers and for executing a request. .. code-block:: cpp struct handler { virtual handler* next(handler* handler) = 0; virtual std::string handle(std::string request) = 0; virtual ~handler() = default; }; The AbstractHandler manages the chain maintenance common to all handlers. .. code-block:: cpp class abstract_handler : public handler { private: handler* next_handler_ = nullptr; public: abstract_handler() : next_handler_(nullptr) { } handler* next(handler* link) override { next_handler_ = link; return link; } std::string handle(std::string request) override { if (next_handler_ != nullptr) { return this->next_handler_->handle(request); } return {}; } }; .. tb-tab:: Implementing classes All Concrete Handlers either handle a request or pass it to the next handler in the chain. .. code-block:: cpp struct monkey_handler : public abstract_handler { std::string handle(std::string request) override { if (request == "Banana") { return "Monkey: I'll eat the " + request + ".\n"; } else { return abstract_handler::handle(request); } } }; struct squirrel_handler : public abstract_handler { std::string handle(std::string request) override { if (request == "Nut") { return "Squirrel: I'll eat the " + request + ".\n"; } else { return abstract_handler::handle(request); } } }; struct dog_handler : public abstract_handler { std::string handle(std::string request) override { if (request == "Meatball") { return "Dog: I'll eat the " + request + ".\n"; } else { return abstract_handler::handle(request); } } }; .. tb-tab:: Client A client uses the handler to process its data. The client passes each item to be process to the handler one at a time, but it unaware that anything other than the handler is involved. .. code-block:: cpp void client(handler& food_handler) { std::vector food = {"Nut", "Banana", "Cup of coffee"}; for (const std::string &snack : food) { std::cout << "Client: Who wants a " << snack << "?\n"; const std::string result = food_handler.handle(snack); if (!result.empty()) { std::cout << " " << result; } else { std::cout << " " << snack << " was left untouched.\n"; } } } int main() { auto monkey = new monkey_handler; auto squirrel = new squirrel_handler; auto dog = new dog_handler; monkey->next(squirrel)->next(dog); client(* monkey); delete monkey; delete squirrel; delete dog; return 0; } However, the linked list of handlers needs to be manually constructed somewhere before it can be used. This gives users flexibility in what is included in the list, but requires knowledge of the internal workings of the chain which are better kept private. And the memory is the responsibility of users to clean up. .. tb-tab:: Run It .. tb-code:: cpp :name: ac_class_design_pattern_chain_of_responsibility_classic #include #include #include struct handler { virtual handler* next(handler* handler) = 0; virtual std::string handle(std::string request) = 0; virtual ~handler() = default; }; class abstract_handler : public handler { private: handler* next_handler_ = nullptr; public: abstract_handler() : next_handler_(nullptr) { } handler* next(handler* link) override { next_handler_ = link; return link; } std::string handle(std::string request) override { if (next_handler_ != nullptr) { return this->next_handler_->handle(request); } return {}; } }; struct monkey_handler : public abstract_handler { std::string handle(std::string request) override { if (request == "Banana") { return "Monkey: I'll eat the " + request + ".\n"; } else { return abstract_handler::handle(request); } } }; struct squirrel_handler : public abstract_handler { std::string handle(std::string request) override { if (request == "Nut") { return "Squirrel: I'll eat the " + request + ".\n"; } else { return abstract_handler::handle(request); } } }; struct dog_handler : public abstract_handler { std::string handle(std::string request) override { if (request == "Meatball") { return "Dog: I'll eat the " + request + ".\n"; } else { return abstract_handler::handle(request); } } }; void client(handler& food_handler) { std::vector food = {"Nut", "Banana", "Cup of coffee"}; for (const std::string &snack : food) { std::cout << "Client: Who wants a " << snack << "?\n"; const std::string result = food_handler.handle(snack); if (!result.empty()) { std::cout << " " << result; } else { std::cout << " " << snack << " was left untouched.\n"; } } } int main() { auto monkey = new monkey_handler; auto squirrel = new squirrel_handler; auto dog = new dog_handler; monkey->next(squirrel)->next(dog); client(* monkey); delete monkey; delete squirrel; delete dog; return 0; } ----- .. admonition:: More to Explore - `Chain of Responsibility Design Pattern `__ on oodesign.com and on :wiki:`Wikipedia `. - Bruce Eckel. Thinking in C++, Vol 2., section 3.3.10.