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

设计模式 专栏收录该内容
15 篇文章 1 订阅

在这里插入图片描述

1.简介

经常听身边的同事说其在项目中用到了责任链模式,今天就来学习一下什么是责任链模式。

责任链模式(Chain of Responsibility Pattern)是设计模式的一种,属于行为型设计模式。

顾名思义,责任链模式为请求创建了一个链,请求在链上被处理。通常某个处理器如果不能处理该请求,那么它会把相同的请求传给链上的下一个处理器。

在这里插入图片描述

2.使用场景

如果一个请求需要经过多个处理步骤,多个处理步骤抽象成一条执行链,那么便可以使用责任链模式。责任链的使用场景一般有:
(1)向多处理器提交一个请求,最终运行时只会有一个处理器处理请求;
(2)向多处理器提交一个请求,所有处理器都会处理请求;

3.示例

在实际场景中,财务审批就是一个责任链模式。假设某个员工需要报销一笔费用,审核者可以分为:

  • Manager:只能审核 1000 元以下的报销;
  • Director:只能审核 10000 元以下的报销;
  • CEO:可以审核任意额度。

使用责任链模式可以实现此报销流程时,每个审核者只关心自己责任范围内的请求,并且处理它。对于超出自己责任范围的,扔给下一个审核者处理,这样,将来继续添加审核者的时候,不用改动现有逻辑。

我们以 C++ 为例实现责任链模式。

首先,我们定义一个在责任链上传递的请求对象:

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;
	}
};

其次,我们在都定义一个处理器抽象类:

class Handler {
public:
	// 返回 1 成功
	// 返回 2 拒绝
	// 返回 0 交下一个处理
	virtual int process(const Request& req) = 0;

	// 返回处理者名称
	virtual string name() = 0;
};

然后,依次实现 ManagerHandler、DirectorHandler 和 CEOHandler。

// 经理审理类
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 组合起来,变成一个链,并通过一个统一入口处理:

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;
	}
};

现在,我们就可以组装出责任链,然后用责任链来处理请求:

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;
}

运行输出:

bob denied by manager
tom approved by manager
alice approved by director
thomas approved by ceo

责任链模式本身很容易理解,需要注意的是,Handler 添加的顺序很重要,如果顺序不对,处理的结果可能就不是符合要求的。

4.变种

此外,责任链模式有很多变种。有些责任链的实现方式是通过某个 Handler 手动调用下一个 Handler 来传递 Request,例如:

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:

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

  • 1
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值