下面的代码有一个Handler类,它遵循经典的责任链模式。但是我们不想为我们想要处理的每一个新类型编写一个新的处理程序类。我试图使Handler成为一个模板类,但它无法工作,因为类Worker是从Handler派生的,因此我求助于使用std::any,现在Handler类一般地处理任何类型。此代码的输出显示了Worker及其派生类型,处理类型为Dish、Inventory和SpeechToMedia的对象。责任链本身已经被允许在从CEO到Dishwasher的级别或从Dishwasher到CEO的级别上工作,而从招聘过程中预先建立的链被认为已经发生了。
#include
#include
#include
#include
#include
#include
#include
class Handler {
protected:
Handler* next = nullptr;
std::any objectHandled;
public:
void setNext (Handler* handler) { next = handler; }
virtual void handle() {
if (next)
next->handle(); // handle() is meant to be overriden, as per the Chain of Responsibility pattern.
}
void resetObjectHandled() { objectHandled.reset(); }
protected:
template void passObjectHandled (T& t) {
next->objectHandled = t;
Handler::handle(); // Do not override! The other key line in the Chain of Responsibility pattern within the each handle() override.
next = nullptr;
}
template void passObjectHandledAndReset (T& t) {
passObjectHandled(t);
objectHandled.reset();
}
};
class Person {
std::string name;
public:
Person (const std::string& n) : name(n) { }
std::string getName() const { return name; }
};
class Dish {
bool isClean;
public:
Dish (bool b = true) : isClean(b) { }
void getsCleaned() { isClean = true; std::cout << "Dirty dish got cleaned.\n"; }
bool getIsClean() const { return isClean; }
};
class Inventory {
bool isDone = false;
public:
void getsDone() { isDone = true; }
bool getIsDone() const { return isDone; }
};
class SpeechToMedia {
bool delivered = false;
public:
bool showDeliveredStatus() const { return delivered; }
void isDelivered() { delivered = true; }
};
class Worker : public Person, public Handler { // Other derived types of Handler will set up their chains in their own way.
protected:
std::vector subordinates;
Worker* boss = nullptr;
public:
using Person::Person;
void addSubordinate (Worker* w) { subordinates.push_back(w); w->setBoss(this); }
void setBoss (Worker* w) { boss = w; }
virtual void stumbleUpon (Dish*) = 0;
virtual void stumbleUpon (Inventory*) = 0;
virtual void stumbleUpon (SpeechToMedia*) = 0;
void setNextHandlerChainAmongSubordinates() { // Choose next Handler* to be whichever subordinate, if any, happens to be physically closest.
if (!subordinates.empty()) {
Worker* subordinateClosest = findClosestSubordinate();
setNext(subordinateClosest);
subordinateClosest->setNextHandlerChainAmongSubordinates();
}
}
void setNextHandlerChainAmongBosses() {
if (boss) { // Even though each worker has only one (immediate) boss, the entire chain should be set all at once just as setNextHandlerChainAmongSubordinates() does.
next = boss;
boss->setNextHandlerChainAmongBosses();
}
}
private:
Worker* findClosestSubordinate() { return subordinates.back(); } // Keeping it simple for now.
};
class Company;
class CEO : public Worker {
Company* company;
struct AnyVisitor {
static const std::unordered_map> map;
};
public:
using Worker::Worker;
CEO (const std::string& name);
Company* getCompany() { return company; }
void stumbleUpon (Dish* dirtyDish) override {
std::cout << getName() << " spots a dirty dish. The washing order is passed on to the nearest manager.\n";
setNextHandlerChainAmongSubordinates();
passObjectHandled(dirtyDish);
}
void stumbleUpon (Inventory* inventory) override {
std::cout << getName() << " passes the inventory paper work to the nearest manager.\n";
setNextHandlerChainAmongSubordinates();
passObjectHandled(inventory);
}
void stumbleUpon (SpeechToMedia* speech) override {
std::cout << getName() << " speaks to the media.\n";
speech->isDelivered();
}
void handle() override {
const auto it = AnyVisitor::map.find(std::type_index(objectHandled.type()));
if (it != AnyVisitor::map.end())
(it->second)(this);
else
Handler::handle();
}
Worker* getManager(int n = 0) const { return subordinates[n]; }
};
const std::unordered_map> CEO::AnyVisitor::map {
{std::type_index(typeid(SpeechToMedia*)), [](CEO* ceo) {
std::cout << ceo->getName() << " speaks to the media.\n";
std::any_cast(ceo->objectHandled)->isDelivered();
ceo->resetObjectHandled();
}
}
};
class Manager : public Worker {
struct AnyVisitor {
static const std::unordered_map> map;
};
public:
using Worker::Worker;
void stumbleUpon (Dish* dirtyDish) override {
std::cout << getName() << " spots a dirty dish. The washing order is passed to the nearest supervisor.\n";
setNextHandlerChainAmongSubordinates();
passObjectHandled(dirtyDish);
}
void stumbleUpon (Inventory* inventory) override {
std::cout << getName() << " passes the inventory paper work to the nearest supervisor.\n";
setNextHandlerChainAmongSubordinates();
passObjectHandled(inventory);
}
void stumbleUpon (SpeechToMedia* speech) override {
std::cout << getName() << " does not speak to the media.\n";
setNextHandlerChainAmongBosses(); // Going up the ranks instead of going down the ranks.
passObjectHandled(speech);
}
void handle() override {
const auto it = AnyVisitor::map.find(std::type_index(objectHandled.type()));
if (it != AnyVisitor::map.end())
(it->second)(this);
else
std::cout << "Error! " << objectHandled.type().name() << " not registered in ManagerAnyVisitor::map.\n";
}
Worker* getSupervisor(int n = 0) const { return subordinates[n]; }
};
// Dish*, Inventory*, SpeechToMedia* are (so far) the types stored in std::any for Worker subtypes, but other derived classes of
// Handler may store other types, so std::variant would not work for Handler's 'objectHandled' data member. Hence std::any is used.
const std::unordered_map> Manager::AnyVisitor::map {
{std::type_index(typeid(Dish*)), [](Manager* manager) {
std::cout << manager->getName() << " does not deal with dirty dishes. The dirty dish is passed to the nearest supervisor\n";
manager->passObjectHandledAndReset(manager->objectHandled);
}
},
{std::type_index(typeid(Inventory*)), [](Manager* manager) {
std::cout << manager->getName() << " does not deal with inventory. The inventory paper work is passed to the nearest supervisor.\n";
manager->passObjectHandledAndReset(manager->objectHandled);
}
},
{std::type_index(typeid(SpeechToMedia*)), [](Manager* manager) {
std::cout << manager->getName() << " does not speak to the media. The speech paper is passed to the CEO.\n";
manager->passObjectHandledAndReset(manager->objectHandled);
}
}
};
class Supervisor : public Worker {
struct AnyVisitor {
static const std::unordered_map> map;
};
public:
using Worker::Worker;
void stumbleUpon (Dish* dirtyDish) override {
std::cout << getName() << " spots a dirty dish. The washing order is passed to the nearest dishwasher.\n";
setNextHandlerChainAmongSubordinates();
passObjectHandled(dirtyDish);
}
void stumbleUpon (Inventory* inventory) override {
std::cout << getName() << " takes care of the inventory paper work.\n";
fillOutInventory(inventory);
}
void stumbleUpon (SpeechToMedia* speech) override {
std::cout << getName() << " does not speak to the media.\n";
setNextHandlerChainAmongBosses();
passObjectHandled(speech);
}
void handle() override {
const auto it = AnyVisitor::map.find(std::type_index(objectHandled.type()));
if (it != AnyVisitor::map.end())
(it->second)(this);
else
std::cout << "Error! " << objectHandled.type().name() << " not registered in SupervisorAnyVisitor::map.\n";
}
void fillOutInventory (Inventory* inventory) { inventory->getsDone(); }
Worker* getDishwasher(int n = 0) const { return subordinates[n]; }
};
const std::unordered_map> Supervisor::AnyVisitor::map {
{std::type_index(typeid(Dish*)), [](Supervisor* supervisor) {
std::cout << supervisor->getName() << " does not deal with dirty dishes. The dirty dish is passed to the nearest dishwasher.\n";
supervisor->passObjectHandledAndReset(supervisor->objectHandled);
}
},
{std::type_index(typeid(Inventory*)), [](Supervisor* supervisor) {
std::cout << supervisor->getName() << " takes care of the inventory paper work.\n";
supervisor->fillOutInventory(std::any_cast(supervisor->objectHandled));
supervisor->resetObjectHandled();
}
},
{std::type_index(typeid(SpeechToMedia*)), [](Supervisor* supervisor) {
std::cout << supervisor->getName() << " does not speak to the media. The speech paper is passed to his manager boss.\n";
supervisor->passObjectHandledAndReset(supervisor->objectHandled);
}
}
};
class Dishwasher : public Worker {
public:
using Worker::Worker;
void stumbleUpon (Dish*) override { std::cout << getName() << " cleans the dirty dish.\n"; }
void handle() override {
std::cout << getName() << " cleans the dirty dish.\n";
std::any_cast(objectHandled)->getsCleaned();
resetObjectHandled();
}
void stumbleUpon (Inventory* inventory) override {
std::cout << getName() << " does not understand inventory work, and gives it to his supervisor.\n";
setNextHandlerChainAmongBosses();
passObjectHandled(inventory);
}
void stumbleUpon (SpeechToMedia* speech) override {
std::cout << getName() << " does not speak to the media. The speech paper is passed to his supervisor.\n";
setNextHandlerChainAmongBosses();
passObjectHandled(speech);
}
};
class Company {
CEO* ceo;
public:
Company (CEO* c) : ceo(c) {
for (int i = 0; i < 3; ++i) {
Manager* manager = new Manager("Manager #" + std::to_string(i+1));
for (int j = 0; j < 4; ++j) {
Supervisor* supervisor = new Supervisor("Supervisor #" + std::to_string(i+1));
manager->addSubordinate(supervisor);
for (int k = 0; k < 5; ++k)
supervisor->addSubordinate(new Dishwasher("Dishwasher #" + std::to_string(i+1)));
}
ceo->addSubordinate(manager);
}
}
// Destructor shall not delete the workers since they should continue to exist after the company is gone (including the CEO).
// In fact, Company's default constructor will not create the workers, but the workers will join the company.
CEO* getCEO() const { return ceo; }
Worker* getManager (int a = 0) { return ceo->getManager(a); }
Worker* getSupervisor (int a = 0, int b = 0) { return dynamic_cast(ceo->getManager(a))->getSupervisor(b); }
Worker* getDishwasher (int a = 0, int b = 0, int c = 0) { return dynamic_cast(dynamic_cast(ceo->getManager(a))->getSupervisor(b))->getDishwasher(c); }
};
CEO::CEO (const std::string& name) : Worker(name), company(new Company(this)) { }
int main() {
CEO ceo("CEO");
Company& company = *ceo.getCompany();
Dish dirtyDish(false), secondDirtyDish(false);
ceo.stumbleUpon(&dirtyDish);
std::cout << "Dish is clean: " << std::boolalpha << dirtyDish.getIsClean() << "\n\n";
company.getManager(1)->stumbleUpon(&secondDirtyDish);
std::cout << "Second dish is clean: " << std::boolalpha << secondDirtyDish.getIsClean() << "\n\n";
Inventory inventory, secondInventory;
ceo.stumbleUpon(&inventory);
std::cout << "Inventory is done: " << std::boolalpha << inventory.getIsDone() << "\n\n";
company.getDishwasher(1,2,3)->stumbleUpon(&secondInventory);
std::cout << "Second inventory is done: " << std::boolalpha << secondInventory.getIsDone() << "\n\n";
SpeechToMedia speech;
company.getDishwasher(1,2,3)->stumbleUpon(&speech);
std::cout << "Speech is delivered: " << std::boolalpha << speech.showDeliveredStatus() << '\n';
}输出:
CEO spots a dirty dish. The washing order is passed on to the nearest manager.
Manager #3 does not deal with dirty dishes. The dirty dish is passed to the nearest supervisor.
Supervisor #3 does not deal with dirty dishes. The dirty dish is passed to the nearest dishwasher.
Dishwasher #3 cleans the dirty dish.
Dirty dish got cleaned.
Dish is clean: true
Manager #2 spots a dirty dish. The washing order is passed to the nearest supervisor.
Supervisor #2 does not deal with dirty dishes. The dirty dish is passed to the nearest dishwasher.
Dishwasher #2 cleans the dirty dish.
Dirty dish got cleaned.
Second dish is clean: true
CEO passes the inventory paper work to the nearest manager.
Manager #3 does not deal with inventory. The inventory paper work is passed to the nearest supervisor.
Supervisor #3 takes care of the inventory paper work.
Inventory is done: true
Dishwasher #2 does not understand inventory work, and gives it to his supervisor.
Supervisor #2 takes care of the inventory paper work.
Second inventory is done: true
Dishwasher #2 does not speak to the media. The speech paper is passed to his supervisor.
Supervisor #2 does not speak to the media. The speech paper is passed to his manager boss.
Manager #2 does not speak to the media. The speech paper is passed to the CEO.
CEO speaks to the media.
Speech is delivered: true发布于 2023-03-17 22:51:23
责任链模式处理的是一个请求,而不是一堆请求。必须处理Dishes、Inventory和Speech使您的代码变得杂乱无章,并增加了复杂性。
您编写的示例也不代表真正的软件工程问题,这进一步加剧了对该模式的使用的混淆。在典型的责任链模式的实现中,应该有3个独立的接口,即WashDishInterface、MaintainInventoryInterface和GiveSpeechInterface。然后您的Ceo、Manager、Supervisor和Dishwasher可以像这样实现接口。
class Dish{};
class WashDishesInterface {
public:
virtual void washDish(Dish & dish)
{
if(!mNext)
mNext->washDish(dish);
}
void setNext(WashDishesInterface* next)
{
mNext = next;
}
private:
WashDishesInterface* mNext;
};
class Speech {};
class GiveSpeechInterface {
public:
virtual void giveSpeech(Speech & speech)
{
if(!mNext)
mNext->giveSpeech(speech);
}
void setNext(GiveSpeechInterface* next)
{
mNext = next;
}
private:
GiveSpeechInterface* mNext;
};
class Ceo : public GiveSpeechInterface, WashDishesInterface
{
public:
void giveSpeech(Speech &) override
{
std::cout << "Ceo gave a speech" << std::endl;
}
private:
};
class Dishwasher : public GiveSpeechInterface, WashDishesInterface
{
public:
void washDish(Dish &) override
{
std::cout << "Dishwasher washed a dish" << std::endl;
}
private:
};Supervior类在哪里结束,Supervisor::AnyVisitor::map初始化是从哪里开始的。delete一切你的new。或者更好地使用智能指针。智能指针还使您能够表示所有权。拥有智能指针成员的类拥有它所指向的对象。具有原始指针成员的类引用对象,但不负责对象的生存期Handler类objectHandled成员变量。相反,您应该让handle函数接受std::any,并去掉创建对象副本并将其存储在Handler中的额外步骤。Worker类Workers作为下属的向量。这很容易出现use-after-free未定义的行为。要么使用单独的类来管理公司层次结构,要么使用std::weak_ptr。与boss记忆变量相同setNextHandlerChainAmongSubordinates()方法,这很容易出错。此外,它还强制一个可以标记为const的方法为非const。当Dishwasher清洗Dish时,唯一真正改变的是盘子的状态,而不是洗碗机。同样适用于setNextHandlerChainAmongBosses()stumbleUpon()方法。我可以看出,在现实世界中,首席执行官可能会无意中遇到一道菜,并将任务委托给他人。但是,在软件开发中,对象不会偶然发现一些东西。这破坏了Single Responsiblilty Principle,现在您的Worker类负责两件事情--遇到请求和处理请求。这设计太糟糕了。Ceo类stumbleUpon()方法。只需将您偶然发现的对象传递给您的handle()方法,让它知道如何处理它。workers.size(),getManager()函数调用未定义的行为(如果n => workers.size() )。因此,如果不调用未定义的行为,则无法使用此函数。使用worker.at(n)进行边界检查。或者更好的是将getManager(int n)更改为std::vector const getManagers(),让调用代码确定它想要哪个管理器。发布于 2023-03-18 12:33:26
发布于 2023-02-20 19:35:06
您的方法有几个问题。我认为这源于这样一个事实:在一些示例代码中看到了责任链的实现方式,并注意到它使用基类的appendNext()或SetNext()成员函数(如维基百科文章)设置下一个处理程序,然后希望使用它来更改处理对象时遍历链的顺序。然而,在构建静态责任链时,这些成员函数只需调用一次。如果您希望它是动态的,那么我根本就不会在setNext()中使用Handler。相反,您可以编写如下的派生类:
class Worker: public Person {
…
Worker* getClosestSubordinate() {
return subordinates.back();
}
Worder* getBoss() {
return boss;
}
…
};
class CEO: public Worker {
…
void stumbleUpon(Dish* dirtyDish) override {
std::cout << getName() << " spots a dirty dish. The washing order is passed on to the nearest manager.\n";
getClosestSubordinate()->stumbleUpon(dirtyDish);
}
void stumbleUpon(SpeechToMedia* speech) override {
std::cout << getName() << " speaks to the media.\n";
speech->isDelivered();
}
};
…
class Dishwasher: public Worker {
…
void stumbleUpon(Dish*) override {
std::cout << getName() << " cleans the dirty dish.\n";
dish->getsCleaned();
}
void stumbleUpon(SpeechToMedia* speech) override {
std::cout << getName() << " does not speak to the media. The speech paper is passed to his supervisor.\n";
getBoss()->stumbleUpon(speech);
}
};注意我们如何缩短派生类中的成员函数,由于一个stumbleUpon()将在必要时直接调用另一个成员函数,所以不需要删除类型,因此不需要std::any。此外,没有必要绘制类型索引的地图。然而,尽管如此,我们并没有失去任何程度的灵活性。
https://codereview.stackexchange.com/questions/283419
复制相似问题