说到责任链设计模式, 我们平时使用的也真是挺多的. 比如: 天天用的网关过滤器, 我们请假的审批流, 打游戏通关, 我们写代码常用的日志打印. 他们都使用了责任链设计模式.
下面就来详细研究一下责任链设计模式
官方定义: 责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。 在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
大白话: 定义中提到的两个主体: 请求的发送者和请求的接收者. 用员工请假来举例. 请求发送者是员工, 请求接收者是主管们. 「对请求的发送者和接收者进行解耦」: 意思就是员工发起请假申请和主管审批请假解耦. 「为请求创建了一个接收者对象的链」: 意思是接收者有多个, 实现了多个接收者进行审批的链条.
下面以一个简单的案例[请假审批流]来介绍责任链的实现
有一个员工小力, 他要请求. 公司规定, 请假3天以内, 直属领导就可以审批. 请假3-10天, 需要部门经理审批. 请假大于10天需要总经理审批.
这个审批流, 我们第一想法是使用if....else....来写.
public void approve(Integer days) {
if (days <= 3) {
// 直属领导审批
} else if (days > 3 && days <= 10) {
// 部门经理审批
} else if (days > 10) {
// 总经理审批
}
}
这样写确实可以实现。 但是他有几个缺点:
既然可能增加多个审批人,我们可以考虑将具体的审批人做成审批者的子类,利用多态来实现。
@Data
public class LeaveRequest {
/**
* 请假的人
*/
private String name;
/**
* 请假的天数
*/
private int days;
public LeaveRequest() {
}
public LeaveRequest(String name, int days) {
this.name = name;
this.days = days;
}
}
有两个属性, 谁请假(name), 请了几天(days).
/**
* 抽象的请假处理类
*/
@Data
public abstract class LeaveHandler {
/**
* 处理人姓名
*/
private String handlerName;
/**
* 下一个处理人
*/
private LeaveHandler nextHandler;
public void setNextHandler(LeaveHandler leaveHandler) {
this.nextHandler = leaveHandler;
}
public LeaveHandler(String handlerName) {
this.handlerName = handlerName;
}
/**
* 具体的处理操作
* @param leaveRequest
* @return
*/
public abstract boolean process(LeaveRequest leaveRequest);
}
这里定义了如下内容:
/**
* 天数小于3天, 直属领导处理
*/
public class DirectLeaveHandler extends LeaveHandler{
public DirectLeaveHandler(String directName) {
super(directName);
}
@Override
public boolean process(LeaveRequest leaveRequest) {
// 随机数大于3则为批准,否则不批准
boolean result = (new Random().nextInt(10)) > 3;
if (!result) {
System.out.println(this.getHandlerName() + "审批驳回");
return false;
} else if (leaveRequest.getDays() <= 3) {
// 审批通过
System.out.println(this.getHandlerName() + "审批完成");
return true;
} else{
System.out.println(this.getHandlerName() + "审批完成");
return this.getNextHandler().process(leaveRequest);
}
}
}
这里模拟了领导审批的流程. 如果小于3天, 直属领导直接审批, 可能通过, 可能不通过. 如果超过3天, 提交给下一级领导审批.
public class ManagerLeaveHandler extends LeaveHandler{
public ManagerLeaveHandler(String name) {
super(name);
}
@Override
public boolean process(LeaveRequest leaveRequest) {
// 随机数大于3则为批准,否则不批准
boolean result = (new Random().nextInt(10)) > 3;
if (!result) {
System.out.println(this.getHandlerName() + "审批驳回");
return false;
} else if (leaveRequest.getDays() > 3 && leaveRequest.getDays() <= 10) {
System.out.println(this.getHandlerName() + "审批完成");
return true;
} else {
System.out.println(this.getHandlerName() + "审批完成");
return this.getNextHandler().process(leaveRequest);
}
}
}
部门经理处理的是3-10天的假期, 如果超过10天, 还要交由下一级领导审批 ** 总经理处理类:
public class GeneralManagerLeavHandler extends LeaveHandler{
public GeneralManagerLeavHandler(String name) {
super(name);
}
@Override
public boolean process(LeaveRequest leaveRequest) {
// 随机数大于3则为批准,否则不批准
boolean result = (new Random().nextInt(10)) > 3;
if (!result) {
System.out.println(this.getHandlerName() + "审批驳回");
return false;
} else {
System.out.println(this.getHandlerName() + "审批完成");
return true;
}
}
}
左右最终流转到总经理的假期都会被审批
public static void main(String[] args) {
DirectLeaveHandler directLeaveHandler = new DirectLeaveHandler("直属主管");
ManagerLeaveHandler managerLeaveHandler = new ManagerLeaveHandler("部门经理");
GeneralManagerLeavHandler generalManagerLeavHandler = new GeneralManagerLeavHandler("总经理");
directLeaveHandler.setNextHandler(managerLeaveHandler);
managerLeaveHandler.setNextHandler(generalManagerLeavHandler);
System.out.println("========张三请假2天==========");
LeaveRequest lxl = new LeaveRequest("张三", 2);
directLeaveHandler.process(lxl);
System.out.println("========李四请假6天==========");
LeaveRequest wangxiao = new LeaveRequest("李四", 6);
directLeaveHandler.process(wangxiao);
System.out.println("========王五请假30天==========");
LeaveRequest yongMing = new LeaveRequest("王五", 30);
directLeaveHandler.process(yongMing);
}
这里我们创建了一个直属领导, 一个部门经理,一个总经理. 并设置了上下级关系. 然后根据员工请假的天数来判断, 应该如何审批. 对于用户而言,他不需要知道前面有多少个领导需要审批. 他只需要提交给第一个领导, 也就是直属领导, 然后不断往下走审批就可以了. 也就是说,在责任链设计模式中,我们只需要拿到链上的第一个处理者,那么链上的每个处理者都有机会处理相应的请求。
以上代码基本上概括了责任链设计模式的使用,但是上述客户端的代码其实也是很繁琐的,后面我们会继续优化责任链设计模式。
由于请假是随机了, 还有可能被驳回. 我们先来看看全部同意的请求结果
========张三请假2天==========
直属主管审批完成
========李四请假6天==========
直属主管审批完成
部门经理审批完成
========王五请假30天==========
直属主管审批完成
部门经理审批完成
总经理审批完成
再来看看有驳回的请求结果
========张三请假2天==========
直属主管审批驳回
========李四请假6天==========
直属主管审批驳回
========王五请假30天==========
直属主管审批完成
部门经理审批驳回
责任链设计模式: 客户端发出一个请求,链上的对象都有机会来处理这一请求,而客户端不需要知道谁是具体的处理对象。多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。 将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止
上面的代码基本上概括了责任链设计模式的使用,但是上述客户端的代码其实也是很繁琐的,后面我优化责任链设计模式。
动态组合,使请求者和接受者解耦。 请求者和接受者松散耦合:请求者不需要知道接受者,也不需要知道如何处理。每个职责对象只负责自己的职责范围,其他的交给后继者。各个组件间完全解耦。 动态组合职责:职责链模式会把功能分散到单独的职责对象中,然后在使用时动态的组合形成链,从而可以灵活的分配职责对象,也可以灵活的添加改变对象职责。
产生很多细粒度的对象:因为功能处理都分散到了单独的职责对象中,每个对象功能单一,要把整个流程处理完,需要很多的职责对象,会产生大量的细粒度职责对象。 不一定能处理:每个职责对象都只负责自己的部分,这样就可以出现某个请求,即使把整个链走完,都没有职责对象处理它。这就需要提供默认处理,并且注意构造链的有效性。
网关有很多功能: API接口限流, 黑名单拦截, 权限验证, 参数过滤等. 下面我们就通过责任链设计模式来实现网关权限控制。
来看一下下面的类图.
可以看到定义了一个抽象的网关处理器. 然后有4个子处理器的实现类.
/**
* 定义抽象的网关处理器类
*/
public abstract class AbstractGatewayHandler {
/**
* 定义下一个网关处理器
*/
protected AbstractGatewayHandler nextGatewayHandler;
public void setNextGatewayHandler(AbstractGatewayHandler nextGatewayHandler) {
this.nextGatewayHandler = nextGatewayHandler;
}
/**
* 抽象网关执行的服务
* @param url
*/
public abstract void service(String url);
}
/**
* API接口限流处理器
*/
public class APILimitGatewayHandler extends AbstractGatewayHandler {
@Override
public void service(String url) {
System.out.println("api接口限流处理, 处理完成");
// 实现具体的限流服务流程
if (this.nextGatewayHandler != null) {
this.nextGatewayHandler.service(url);
}
}
}
/**
* 黑名单处理器
*/
public class BlankListGatewayHandler extends AbstractGatewayHandler {
@Override
public void service(String url) {
System.out.println("黑名单处理, 处理完成");
// 实现具体的限流服务流程
if (this.nextGatewayHandler != null) {
this.nextGatewayHandler.service(url);
}
}
}
/**
* 权限验证处理器
*/
public class PermissionValidationGatewayHandler extends AbstractGatewayHandler {
@Override
public void service(String url) {
System.out.println("权限验证处理, 处理完成");
// 实现具体的限流服务流程
if (this.nextGatewayHandler != null) {
this.nextGatewayHandler.service(url);
}
}
}
/**
* 参数校验处理器
*/
public class ParameterVerificationGatewayHandler extends AbstractGatewayHandler {
@Override
public void service(String url) {
System.out.println("参数校验处理, 处理完成");
// 实现具体的限流服务流程
if (this.nextGatewayHandler != null) {
this.nextGatewayHandler.service(url);
}
}
}
/**
* 网关客户端
*/
public class GatewayClient {
public static void main(String[] args) {
APILimitGatewayHandler apiLimitGatewayHandler = new APILimitGatewayHandler();
BlankListGatewayHandler blankListGatewayHandler = new BlankListGatewayHandler();
ParameterVerificationGatewayHandler parameterVerificationGatewayHandler = new ParameterVerificationGatewayHandler();
PermissionValidationGatewayHandler permissionValidationGatewayHandler = new PermissionValidationGatewayHandler();
apiLimitGatewayHandler.setNextGatewayHandler(blankListGatewayHandler);
blankListGatewayHandler.setNextGatewayHandler(parameterVerificationGatewayHandler);
parameterVerificationGatewayHandler.setNextGatewayHandler(permissionValidationGatewayHandler);
apiLimitGatewayHandler.service("http://www.baidu.com");
}
}
这里和之前差不多, 不做太多解释了, 来看运行效果:
api接口限流处理, 处理完成
黑名单处理, 处理完成
参数校验处理, 处理完成
权限验证处理, 处理完成
这样就进行了一系列的网关处理. 当然, 每一次处理都应该返回处理结果, 然后决定是否进行下一次处理. 这里就简化了
在第三步网关客户端中,对责任链进行了初始化操作。 这样, 每次客户端想要发起请求都需要执行一遍初始化操作, 其实完全没有这个必要. 我们可以使用工厂设计模式, 将客户端抽取到工厂中, 每次只需要拿到链上的第一个处理者就可以了.
/**
* 网关处理器工厂
*/
public class GatewayHandlerFactory {
public static AbstractGatewayHandler getFirstGatewayHandler() {
APILimitGatewayHandler apiLimitGatewayHandler = new APILimitGatewayHandler();
BlankListGatewayHandler blankListGatewayHandler = new BlankListGatewayHandler();
ParameterVerificationGatewayHandler parameterVerificationGatewayHandler = new ParameterVerificationGatewayHandler();
PermissionValidationGatewayHandler permissionValidationGatewayHandler = new PermissionValidationGatewayHandler();
apiLimitGatewayHandler.setNextGatewayHandler(blankListGatewayHandler);
blankListGatewayHandler.setNextGatewayHandler(parameterVerificationGatewayHandler);
parameterVerificationGatewayHandler.setNextGatewayHandler(permissionValidationGatewayHandler);
return apiLimitGatewayHandler;
}
}
网关处理器工厂定义了各个网关处理器之间的关系, 并返回第一个网关处理器.
/**
* 网关客户端
*/
public class GatewayClient {
public static void main(String[] args) {
GatewayHandlerFactory.getFirstGatewayHandler().service("http://www.baidu.com");
}
}
我们在客户端只需要直接调用第一个网关处理器就可以了, 不需要关心其他的处理器.