【设计形式攻略】行为型模式之Chain of Responsibility模式
【设计模式攻略】行为型模式之Chain of Responsibility模式
概要

程序中经常出现这样的逻辑,收到XX请求,进行XX相关的响应处理,收到YY请求,则进行YY的响应处理。请求与响应之间彼此配对,所以代码也往往会为这种配对提供一对一的对应关系。比如说之前说过的Command模式中,一种Command会跟一个Performer对应起来确保这种逻辑关系。那如果某个请求该如何响应是未知或动态决定的,如何处理呢?答案之一,用一堆条件来判断限制啊?很多情况下,没错,应该这样做。但有时这样做会不会太累啊?有没有什么其他方案呢?Chain
of Responsibility模式就是选择之一,通过对所有的响应方建立职责链,请求方不需要知道由哪个响应来执行,只需要把请求丢给职责链,职责链的每个元素都有可以执行,可以传递,当然也可以中止传递。
目的
避免请求方耦合于执行方,请求方可以不需要知道哪个目标会执行该请求,而只需要把请求传递给职责链就ok。执行方被动态串联形成职责链,执行条件由每个元素自己控制。
实例
想到这样一个伪例子,当收到的命令小于Command1或者等于Command3时,执行Performer1,当命令大于Command1,小于Command5并且不等于Command3时,执行Performer2,当命令等于Command7时,也执行Performer2,当命令大于Command5,小于Command9并且不等于Command7时,执行Performer3。好纠结的条件啊,其实代码倒还好,看下实现:
void handle_request(int cmd) { if (cmd <= COMMAND_1) { perform1.action(); } else if (cmd <= COMMAND_5) { if (cmd == COMMAND_3) { perform1.action(); } else { perform2.action(); } } else if (cmd <= COMMAND_9) { if (cmd == COMMAND_7) { perform2.action(); } else { perform3.action(); } } else { } }
确实处理逻辑还不算太负责,但是万一又来n个条件逻辑呢,所以代码只会变得越来越复杂,perform的触发方会很纠结与这种代码的维护与测试。那用Chain of Responsibility模式呢?怎么实现?先类图设计:
太抽象了,还是直接看下代码怎么实现的,首先Handler类提供handle_request接口,并提供set_successor方法可以设置职责链上下一个Handler是谁,每个具体的Handler子类实现自己的handle_request,其中关注某个performer运行需要的条件就可以了,当然也别忘了在不符合条件时把cmd传给下一个Handler。
class Handler { public: virtual void handle_request(int cmd) = 0; public void set_successor(Handler* successor) { m_successor = successor; } protected: Handler* m_successor; }; class ConcreteHandler1 : public Handler { public: void handle_request(int cmd) { if (cmd <= COMMAND_1 || cmd == COMMAND_3) { performer1.action(); } else if (m_successor != NULL) { m_successor->handle_request(cmd); } } }; class ConcreteHandler2 : public Handler { public: void handle_request(int cmd) { if (cmd <= COMMAND_5 || cmd == COMMAND_7) { performer2.action(); } else if (m_successor != NULL) { m_successor->handle_request(cmd); } } }; class ConcreteHandler3 : public Handler { public: void handle_request(int cmd) { if (cmd <= COMMAND_9) { performer3.action(); } else if (m_successor != NULL) { m_successor->handle_request(cmd); } } };
具体使用可以如下,都可以通过h1来处理request,最后cmd都会由对应的Handler进行处理:
Handler* h1 = new ConcreteHandler1(); Handler* h2 = new ConcreteHandler2(); Handler* h3 = new ConcreteHandler3(); h1->setSuccessor(h2); h2->setSuccessor(h3); h1->handle_request(COMMAND_1); h1->handle_request(COMMAND_3); h1->handle_request(COMMAND_5); h1->handle_request(COMMAND_7); h1->handle_request(COMMAND_9);
可能有人会发现,每个handle_request都需要去关心通过m_successor来转发有点麻烦,万一哪个地方忘了处理,那职责链就断了,让我们看看是否可以想点办法来改善这一点,看下如下代码是不是让实现更简单更健壮了:
class Handler { public: void handle_request(int cmd) { if (handle_request_impl(cmd) == false && m_successor != NULL) { m_successor->handle_request(cmd); } } virtual bool handle_request_impl(int cmd) = 0; public void set_successor(Handler* successor) { m_successor = successor; } private: Handler* m_successor; }; class ConcreteHandler1 : public Handler { public: bool handle_request_impl(int cmd) { if (cmd <= COMMAND_1 || cmd == COMMAND_3) { perform1.action(); return true ; } return false ; } }; class ConcreteHandler2 : public Handler { public: bool handle_request_impl(int cmd) { if (cmd <= COMMAND_5 || cmd == COMMAND_7) { perform2.action(); return true ; } return false ; } }; class ConcreteHandler3 : public Handler { public: bool handle_request_impl(int cmd) { if (cmd <= COMMAND_9) { perform3.action(); return true ; } return false ; } };
应用
除了以上示例中的例子,实际应用中有时还希望一个请求可能被多个Handler处理的情况,针对这种情况其实只要稍微修改下代码,控制handle_request_impl的返回值就可以做到。另外,如果不需要由调用方来调用set_successor组成职责链,那么甚至可以在每创建一个Handler时自动进入职责链,从而把set_successor的细节也隐藏掉,如何实现的话,自己尝试吧。