Паттерн Цепочка обязанностей на C++
Цепочка обязанностей – это поведенческий паттерн, позволяющий передавать запрос по цепочке потенциальных обработчиков, пока один из них не обработает запрос.
Избавляет от жёсткой привязки отправителя запроса к его получателю, позволяя выстраивать цепь из различных обработчиков динамически.
Особенности паттерна на 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.