文章目录
- 1.简介
- 2.使用场景
- 3.示例
- 4.变种
- 参考文献
1.简介
经常听身边的同事说其在项目中用到了责任链模式,今天就来学习一下什么是责任链模式。
责任链模式(Chain of Responsibility)是设计模式的一种,属于行为型设计模式。
顾名思义,责任链模式为请求创建了一个链,请求在链上被处理。通常某个处理器如果不能处理该请求,那么它会把相同的请求传给链上的下一个处理器。
2.使用场景
如果一个请求需要经过多个处理步骤,多个处理步骤抽象成一条执行链,那么便可以使用责任链模式。责任链的使用场景一般有: (1)向多处理器提交一个请求,最终运行时只会有一个处理器处理请求; (2)向多处理器提交一个请求,所有处理器都会处理请求;
3.示例
在实际场景中,财务审批就是一个责任链模式。假设某个员工需要报销一笔费用,审核者可以分为:
- Manager:只能审核 1000 元以下的报销;
- Director:只能审核 10000 元以下的报销;
- CEO:可以审核任意额度。
使用责任链模式可以实现此报销流程时,每个审核者只关心自己责任范围内的请求,并且处理它。对于超出自己责任范围的,扔给下一个审核者处理,这样,将来继续添加审核者的时候,不用改动现有逻辑。
我们以 C 为例实现责任链模式。
首先,我们定义一个在责任链上传递的请求对象:
代码语言:javascript复制class Request {
string name;
double amount;
public:
Request(string name, double amount) {
this->name = name;
this->amount = amount;
}
string getName() const {
return name;
}
double getAmount() const {
return amount;
}
};
其次,我们在都定义一个处理器抽象类:
代码语言:javascript复制class Handler {
public:
// 返回 1 成功
// 返回 2 拒绝
// 返回 0 交下一个处理
virtual int process(const Request& req) = 0;
// 返回处理者名称
virtual string name() = 0;
};
然后,依次实现 ManagerHandler、DirectorHandler 和 CEOHandler。
代码语言:javascript复制// 经理审理类
class ManagerHandler : public Handler {
public:
int process(const Request& req) {
// 如果超过 1000 元处理不了,交下一个处理
if (req.getAmount() > 1000) {
return 0;
}
// 对 Bob 有偏见
return req.getName() == "bob" ? 2 : 1;
}
string name() {
return "manager";
}
};
// 主任审批类
class DirectorHandler: public Handler {
public:
int process(const Request& req) {
// 如果超过 10000 元 处理不了,交下一个处理
if (req.getAmount() > 10000) {
return 0;
}
return 1;
}
string name() {
return "director";
}
};
// 总裁审批类
class CEOHandler : public Handler {
public:
int process(const Request& req) {
return 1;
}
string name() {
return "ceo";
}
};
有了不同的 Handler 后,我们还要把这些 Handler 组合起来,变成一个链,并通过一个统一入口处理:
代码语言:javascript复制class HandlerChain {
// 持有所有 Handler
list<Handler*> handlers;
public:
void add(Handler* h) {
handlers.push_back(h);
}
int process(const Request& req) {
// 依次调用每个 Handler
for (auto h : handlers) {
int r = h->process(req);
if (r != 0) {
// 如果返回 1 或 2,处理结束
cout << req.getName() " " (r == 1 ? "approved by " : "denied by ") h->name() << endl;
return r;
}
}
cout << "process failed" << endl;
return -1;
}
};
现在,我们就可以组装出责任链,然后用责任链来处理请求:
代码语言:javascript复制int main()
{
HandlerChain chain;
chain.add(new ManagerHandler());
chain.add(new DirectorHandler());
chain.add(new CEOHandler());
chain.process(Request("bob", 100));
chain.process(Request("tom", 1000));
chain.process(Request("alice", 10000));
chain.process(Request("thomas", 100000));
return 0;
}
运行输出:
代码语言:javascript复制bob denied by manager
tom approved by manager
alice approved by director
thomas approved by ceo
责任链模式本身很容易理解,需要注意的是,Handler 添加的顺序很重要,如果顺序不对,处理的结果可能就不是符合要求的。
4.变种
此外,责任链模式有很多变种。有些责任链的实现方式是通过某个 Handler 手动调用下一个 Handler 来传递 Request,例如:
代码语言:javascript复制class AHandler: public Handler {
Handler next;
public:
void process(const Request& req) {
if (!canProcess(req)) {
// 手动交给下一个Handler处理
next.process(req);
} else {
...
}
}
};
还有一些责任链模式,每个 Handler 都有机会处理 Request,通常这种责任链被称为拦截器(Interceptor)或过滤器(Filter),它的目的不是找到某个 Handler 处理掉 Request,而是每个Handler都做一些工作,比如:
- 记录日志;
- 检查权限;
- 监控上报;
- 分布式追踪;
- …
理解拦截器的原理关键点在于理解拦截器的触发时机以及顺序性。
触发时机: 拦截器可以拦截到接口的请求和响应,并对请求,响应,上下文进行处理(用通俗的语言阐述也就是 可以在请求接受前做一些事情,请求处理后做一些事情),因此,拦截器从功能上说是分为两个部分的 前置(业务逻辑处理前) 和 后置(业务逻辑处理后)。
顺序性: 拦截器是有明确的顺序性,根据拦截器的注册顺序依次执行前置部分逻辑,并逆序执行拦截器的后置部分。如下图所示:
例如,JavaEE 的 Servlet 规范定义的 Filter 就是一种责任链模式,它不但允许每个 Filter 都有机会处理请求,还允许每个 Filter 决定是否将请求“放行”给下一个 Filter:
代码语言:javascript复制public class AuditFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
log(req);
if (check(req)) {
// 放行:
chain.doFilter(req, resp);
} else {
// 拒绝:
sendError(resp);
}
}
}
这种模式不但允许一个 Filter 自行决定处理 ServletRequest 和 ServletResponse,还可以“伪造” ServletRequest 和 ServletResponse 以便让下一个 Filter 处理,能实现非常复杂的功能。
参考文献
[1] 廖雪峰.责任链 [2] 程杰.大话设计模式.C24:职责链模式.P245-256