命令链(Chain of Command)。
责任链是一种行为设计模式,允许你将请求沿着处理者链进行发送。收到请求后,每个处理者均可对请求进行处理,或将其传递给链上的下个处理者。
假如你正在开发一个在线订购系统。你希望对系统访问进行限制,只允许认证用户创建订单。此外,拥有管理权限的用户也拥有所有订单的完全访问权限。
简单规划后,你会意识到这些检查必须依次进行。只要接收到包含用户凭据的请求,应用程序就可尝试对进入系统的用户进行认证。但如果由于用户凭据不正确而导致认证失败,那就没有必要进行后续检查了。
随着功能不断迭代,检查代码逐渐变得越来越混乱,修改某个检查步骤有时会影响其他的检查步骤。最糟糕的是,当你希望复用这些检查步骤来保护其他系统组件时,你只能复制部分代码,因为这些组件只需要部分而非全部的检查步骤。
系统会变得让人非常费解,而且其维护成本也会激增。你在艰难地和这些代码共处一段时间后,有一天终于决定对整个系统进行重构。
与许多其他行为设计模式一样,「责任链模式」会将特定行为转换为被称作处理者的独立对象。在上述示例中,每个检查步骤都可被抽取为仅有单个方法的类,并执行检查操作。请求及其数据则会被作为参数传递给该方法。
模式建议你将这些处理者连成一条链。链上的每个处理者都有一个成员变量来保存对于下一处理者的引用。除了处理请求外,处理者还负责沿着链传递请求。请求会在链上移动,直至所有处理者都有机会对其进行处理。
最重要的是:处理者可以决定不再沿着链传递请求,这可高 效地取消所有后续处理步骤。
在我们的订购系统示例中,处理者会在进行请求处理工作后决定是否继续沿着链传递请求。如果请求中包含正确的数据,所有处理者都将执行自己的主要行为,无论该行为是身份验证还是数据缓存。
不过还有一种稍微不同的方式(也是更经典一种),那就是处理者接收到请求后自行决定是否能够对其进行处理。如果自己能够处理,处理者就不再继续传递请求。因此在这种情况下,每个请求要么最多有一个处理者对其进行处理,要么没有任何处理者对其进行处理。在处理图形用户界面元素栈中的事件时,这种方式非常常见。
例如, 当用户点击按钮时, 按钮产生的事件将沿着 GUI 元素链进行传递, 最开始是按钮的容器 (如窗体或面板), 直至应用程序主窗口。 链上第一个能处理该事件的元素会对其进行处理。 此外, 该例还有另一个值得我们关注的地方: 它表明我们总能从对象树中抽取出链来。
所有处理者类均实现同一接口是关键所在。每个具体处理者仅关心下一个包含 execute 执行 方法的处理者。 这样一来,你就可以在运行时使用不同的处理者来创建链,而无需将相关代码与处理者的具体类进行耦合。
最近, 你刚为自己的电脑购买并安装了一个新的硬件设备。 身为一名极客, 你显然在电脑上安装了多个操作系统, 所以你会试着启动所有操作系统来确认其是否支持新的硬件设备。 Windows 检测到了该硬件设备并对其进行了自动启用。 但是你喜爱的 Linux 系统并不支持新硬件设备。 抱着最后一点希望, 你决定拨打包装盒上的技术支持电话。
首先你会听到自动回复器的机器合成语音, 它提供了针对各种问题的九个常用解决方案, 但其中没有一个与你遇到的问题相关。 过了一会儿, 机器人将你转接到人工接听人员处。
这位接听人员同样无法提供任何具体的解决方案。 他不断地引用手册中冗长的内容, 并不会仔细聆听你的回应。 在第 10 次听到 “你是否关闭计算机后重新启动呢?” 这句话后, 你要求与一位真正的工程师通话。
最后, 接听人员将你的电话转接给了工程师, 他或许正缩在某幢办公大楼的阴暗地下室中, 坐在他所深爱的服务器机房里, 焦躁不安地期待着同一名真人交流。 工程师告诉了你新硬件设备驱动程序的下载网址, 以及如何在 Linux 系统上进行安装。 问题终于解决了! 你挂断了电话, 满心欢喜。
该模式能将多个处理者连接成一条链。接收到请求后,它会“询问”每个处理者是否能够对其进行处理。这样所有处理者都有机会来处理请求。
无论你以何种顺序将处理者连接成一条链,所有请求都会严格按照顺序通过链上的处理者。
如果在处理者类中有对引用成员变量的设定方法,你将能动态地插入和移除处理者,或者改变其顺序。
部分请求可能未被处理。
在本例中,员工申请处理票据需要上报给上级,如果上级无权处理就上报给更高的上级。
Handler.h:
#ifndef HANDLER_H_
#define HANDLER_H_
// 抽象处理者, 在C++中是抽象基类
class ApproverInterface {
public:
// 添加上级
virtual void setSuperior(ApproverInterface* superior) = 0;
// 处理票据申请, 参数是票据面额
virtual void handleRequest(double amount) = 0;
};
#endif // HANDLER_H_
BaseHandler.h:
#ifndef BASE_HANDLER_H_
#define BASE_HANDLER_H_
#include
#include "Handler.h"
class BaseApprover : public ApproverInterface {
public:
BaseApprover(double mpa, std::string n) : max_processible_amount_(mpa), name_(n), superior_(nullptr) {}
// 设置上级
void setSuperior(ApproverInterface* superior) {
superior_ = superior;
}
// 处理票据
void handleRequest(double amount) {
// 可处理时直接处理即可
if (amount <= max_processible_amount_) {
printf("%s处理了该票据, 票据面额:%f\n", name_.c_str(), amount);
return;
}
// 无法处理时移交给上级
if (superior_ != nullptr) {
printf("%s无权处理, 转交上级...\n", name_.c_str());
superior_->handleRequest(amount);
return;
}
// 最上级依然无法处理时报错
printf("无人有权限处理该票据, 票据金额:%f\n", name_.c_str(), amount);
}
private:
double max_processible_amount_; // 可处理的最大面额
std::string name_;
ApproverInterface* superior_;
};
#endif // BASE_HANDLER_H_
ConcreteHandler.h:
#ifndef CONCRETE_HANDLER_H_
#define CONCRETE_HANDLER_H_
#include
#include
#include "BaseHandler.h"
// 具体处理者: 组长(仅处理面额<=10的票据)
class GroupLeader : public BaseApprover {
public:
explicit GroupLeader(std::string name) : BaseApprover(10, name) {}
};
// 具体处理者: 经理(仅处理面额<=100的票据)
class Manager : public BaseApprover {
public:
explicit Manager(std::string name) : BaseApprover(100, name) {}
};
// 具体处理者: 老板(仅处理面额<=1000的票据)
class Boss : public BaseApprover {
public:
explicit Boss(std::string name) : BaseApprover(1000, name) {}
};
#endif // CONCRETE_HANDLER_H_
main.cpp:
#include "ConcreteHandler.h"
int main() {
// 请求处理者: 组长、经理和老板
GroupLeader* group_leader = new GroupLeader("张组长");
Manager* manager = new Manager("王经理");
Boss* boss = new Boss("李老板");
// 设置上级
group_leader->setSuperior(manager);
manager->setSuperior(boss);
// 不同面额的票据统一先交给组长审批
group_leader->handleRequest(8);
group_leader->handleRequest(88);
group_leader->handleRequest(888);
group_leader->handleRequest(8888);
delete group_leader;
delete manager;
delete boss;
return 0;
}
编译运行:
$g++ main.cpp -std=c++11 -o chainofresponsibility
$./chainofresponsibility
张组长处理了该票据, 票据面额:8.000000
张组长无权处理, 转交上级...
王经理处理了该票据, 票据面额:88.000000
张组长无权处理, 转交上级...
王经理无权处理, 转交上级...
李老板处理了该票据, 票据面额:888.000000
张组长无权处理, 转交上级...
王经理无权处理, 转交上级...
无人有权限处理该票据, 票据金额:8888.000000