前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >行为型设计模式(1)——责任链模式(Chain of Responsibility)

行为型设计模式(1)——责任链模式(Chain of Responsibility)

作者头像
恋喵大鲤鱼
发布2021-06-17 19:56:55
3580
发布2021-06-17 19:56:55
举报
文章被收录于专栏:C/C++基础C/C++基础
在这里插入图片描述
在这里插入图片描述

文章目录

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-06-14 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 1.简介
  • 2.使用场景
  • 3.示例
  • 4.变种
  • 参考文献
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档