Цепочка обязанностей на C++
Цепочка обязанностей — это поведенческий паттерн, позволяющий передавать запрос по цепочке потенциальных обработчиков, пока один из них не обработает запрос.
Избавляет от жёсткой привязки отправителя запроса к его получателю, позволяя выстраивать цепь из различных обработчиков динамически.
Сложность:
Популярность:
Применимость: Паттерн встречается в C++ не так уж часто, так как для его применения нужна цепь объектов, например, связанный список.
Признаки применения паттерна: Цепочку обязанностей можно определить по спискам обработчиков или проверок, через которые пропускаются запросы. Особенно если порядок следования обработчиков важен.
Концептуальный пример
Этот пример показывает структуру паттерна Цепочка обязанностей, а именно — из каких классов он состоит, какие роли эти классы выполняют и как они взаимодействуют друг с другом.
main.cc: Пример структуры паттерна
/** * Интерфейс Обработчика объявляет метод построения цепочки обработчиков. Он * также объявляет метод для выполнения запроса. */ class Handler { public: virtual Handler *SetNext(Handler *handler) = 0; virtual std::string Handle(std::string request) = 0; }; /** * Поведение цепочки по умолчанию может быть реализовано внутри базового класса * обработчика. */ class AbstractHandler : public Handler { /** * @var Handler */ private: Handler *next_handler_; public: AbstractHandler() : next_handler_(nullptr) { } Handler *SetNext(Handler *handler) override { this->next_handler_ = handler; // Возврат обработчика отсюда позволит связать обработчики простым способом, // вот так: // $monkey->setNext($squirrel)->setNext($dog); return handler; } std::string Handle(std::string request) override { if (this->next_handler_) { return this->next_handler_->Handle(request); } return {}; } }; /** * Все Конкретные Обработчики либо обрабатывают запрос, либо передают его * следующему обработчику в цепочке. */ class MonkeyHandler : public AbstractHandler { public: std::string Handle(std::string request) override { if (request == "Banana") { return "Monkey: I'll eat the " + request + ".\n"; } else { return AbstractHandler::Handle(request); } } }; class SquirrelHandler : public AbstractHandler { public: std::string Handle(std::string request) override { if (request == "Nut") { return "Squirrel: I'll eat the " + request + ".\n"; } else { return AbstractHandler::Handle(request); } } }; class DogHandler : public AbstractHandler { public: std::string Handle(std::string request) override { if (request == "MeatBall") { return "Dog: I'll eat the " + request + ".\n"; } else { return AbstractHandler::Handle(request); } } }; /** * Обычно клиентский код приспособлен для работы с единственным обработчиком. В * большинстве случаев клиенту даже неизвестно, что этот обработчик является * частью цепочки. */ void ClientCode(Handler &handler) { std::vector<std::string> food = {"Nut", "Banana", "Cup of coffee"}; for (const std::string &f : food) { std::cout << "Client: Who wants a " << f << "?\n"; const std::string result = handler.Handle(f); if (!result.empty()) { std::cout << " " << result; } else { std::cout << " " << f << " was left untouched.\n"; } } } /** * Другая часть клиентского кода создает саму цепочку. */ int main() { MonkeyHandler *monkey = new MonkeyHandler; SquirrelHandler *squirrel = new SquirrelHandler; DogHandler *dog = new DogHandler; monkey->SetNext(squirrel)->SetNext(dog); /** * Клиент должен иметь возможность отправлять запрос любому обработчику, а не * только первому в цепочке. */ std::cout << "Chain: Monkey > Squirrel > Dog\n\n"; ClientCode(*monkey); std::cout << "\n"; std::cout << "Subchain: Squirrel > Dog\n\n"; ClientCode(*squirrel); delete monkey; delete squirrel; delete dog; return 0; } Output.txt: Результат выполнения
Chain: Monkey > Squirrel > Dog Client: Who wants a Nut? Squirrel: I'll eat the Nut. Client: Who wants a Banana? Monkey: I'll eat the Banana. Client: Who wants a Cup of coffee? Cup of coffee was left untouched. Subchain: Squirrel > Dog Client: Who wants a Nut? Squirrel: I'll eat the Nut. Client: Who wants a Banana? Banana was left untouched. Client: Who wants a Cup of coffee? Cup of coffee was left untouched.