一、责任链模式介绍
1、责任链模式介绍
职责链模式(chain of responsibility pattern) 定义: 避免将一个请求的发送者与接收者耦合在
一起,让多个对象都有机会处理请求。将接收请求的对象连接成一条链,并且沿着这条链
传递请求,直到有一个对象能够处理它为止。
在职责链模式中,多个处理器(也就是刚刚定义中说的“接收对象”)依次处理同一个请 求。
一个请求先经过 A 处理器处理,然后再把请求传递给 B 处理器,B 处理器处理完后再 传递
给 C 处理器,以此类推,形成一个链条。链条上的每个处理器各自承担各自的处理职 责,
所以叫作职责链模式。如下图所示:
二、责任链模式原理
1、责任链模式结构
责任链模式结构图如下:
责任链模式包含如下角色:
1)抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和
一个后继连接(链上的每个处理者都有一个成员变量来保存对于下一处理者的引用,
比如上图中的successor) 。
2)具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否
处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
3)客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不
关心处理细节和请求的传递过程。
2、责任链模式用代码表示
责任链模式的实现非常简单,每一个具体的处理类都会保存在它之后的下一个处理类。
当处理完成后,就会调用设置好的下一个处理类,直到最后一个处理类不再设置下一个
处理类,这时处理链条全部完成。
/*******************************************************
* 封装请求数据
*
* @author lbf
*
*******************************************************/
public class RequestData {
private String data;
public RequestData(String data) {
this.data = data;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
}
/*******************************************************
* 抽象处理者类
*
* @author lbf
*
*******************************************************/
public abstract class Handler {
//下一处理者的引用
protected Handler successor = null;
public void setSuccessor(Handler successor){
this.successor = successor;
}
//抽象方法,由具体处理者实现,
// 每个具体处里者的行为可能都不相同
public abstract void handle(RequestData requestData);
}
/*******************************************************
* 具体处里者A
* @author lbf
* @date 2024-11-10 21:26
*******************************************************/
public class HandlerA extends Handler{
@Override
public void handle(RequestData requestData) {
System.out.println("HandlerA 执行代码逻辑! 处理: " + requestData.getData());
requestData.setData(requestData.getData().replace("A",""));
if(successor != null){
successor.handle(requestData);
}else{
System.out.println("执行中止!");
}
}
}
/*******************************************************
* 具体处理者B
* @author lbf
*
*******************************************************/
public class HandlerB extends Handler{
@Override
public void handle(RequestData requestData) {
System.out.println("HandlerB 执行代码逻辑! 处理: " + requestData.getData());
requestData.setData(requestData.getData().replace("B",""));
if(successor != null){
successor.handle(requestData);
}else{
System.out.println("执行中止!");
}
}
}
/*******************************************************
* 具体处理者C
* @author lbf
*
*******************************************************/
public class HandlerC extends Handler{
@Override
public void handle(RequestData requestData) {
System.out.println("HandlerC 执行代码逻辑! 处理: " + requestData.getData());
requestData.setData(requestData.getData());
if(successor != null){
successor.handle(requestData);
}else{
System.out.println("执行中止!");
}
}
}
/*******************************************************
*
* @author lbf
*
*******************************************************/
public class Test {
public static void main(String[] args) {
Handler h1 = new HandlerA();
Handler h2 = new HandlerB();
Handler h3 = new HandlerC();
h1.setSuccessor(h2);
h2.setSuccessor(h3);
RequestData requestData = new RequestData("请求数据ABCDE");
h1.handle(requestData);
}
}
三、责任链模式使用示例
下面以业务审批流程为例,来看下责任链模式的使用:
审批的过程会有不同级别的负责人加入进行审批(平常系统上线只需三级负责人审批即可,
但在一些特殊日期会有其他层级的人临时加入审批,如:双十一前后需要二级或一级审核
人参与审批),接下来我们就使用职责链模式来设计一下此功能。
1、不使用设计模式实现
多个负责人的审批流程都在一个方法中按先后顺序完成,示例代码如下:
/*******************************************************
* 审核信息
* @author lbf
*
*******************************************************/
public class AuthInfo {
private String code;
private String info ="";
public AuthInfo(String code, String... infos) {
this.code = code;
for (String str : infos) {
info = this.info.concat(str +" ");
}
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
@Override
public String toString() {
return "AuthInfo{" +
"code='" + code + '\'' +
", info='" + info + '\'' +
'}';
}
}
/*******************************************************
* 模拟审核服务
* @author lbf
*
*******************************************************/
public class AuthService {
//审批信息 审批人Id+申请单Id
private static Map<String, Date> authMap = new HashMap<String, Date>();
/**
* 审核流程
* @param uId 审核人id
* @param orderId 审核单id
*/
public static void auth(String uId, String orderId){
System.out.println("进入审批流程,审批人ID: " + uId);
authMap.put(uId.concat(orderId),new Date());
}
//查询审核结果
public static Date queryAuthInfo(String uId, String orderId){
return authMap.get(uId.concat(orderId)); //key=审核人id+审核单子id
}
}
/*******************************************************
* 审核接口
* @author lbf
*
*******************************************************/
public class AuthController {
//审核
public AuthInfo doAuth(String name, String orderId, Date authDate) throws ParseException {
//三级审批
Date date = null;
//查询是否存在审核信息,查询条件: 审核人ID+订单ID,返回Map集合中的Date
date = AuthService.queryAuthInfo("1000013", orderId);
//如果为空,封装AuthInfo信息(待审核)返回
if(date == null){
return new AuthInfo("0001","单号: "+orderId,"状态: 等待三级审批负责人进行审批");
}
//二级审批
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 时间格式化
//二级审核人主要审核双十一之前, 11-01 ~ 11-10号的请求,所以要对传入的审核时间进行判断
//审核时间 大于 2022-11-01 并且 小于 2022-11-10,Date1.after(Date2),当Date1大于Date2时,返回TRUE,Date1.before(Date2),当Date1小于Date2时,返回TRUE
if(authDate.after(f.parse("2022-11-01 00:00:00")) && authDate.before(f.parse("2022-11-10 00:00:00"))){
//条件成立,查询二级审核的审核信息
date = AuthService.queryAuthInfo("1000012",orderId);
//如果为空,还是待二级审核人审核状态
if(date == null){
return new AuthInfo("0001","单号: "+orderId,"状态: 等待二级审批负责人进行审批");
}
}
//一级审批
//审核范围是在11-11日 ~ 11-31日
if(authDate.after(f.parse("2022-11-11 00:00:00")) && authDate.before(f.parse("2022-11-31 00:00:00"))){
date = AuthService.queryAuthInfo("1000011",orderId);
if(date == null){
return new AuthInfo("0001","单号: "+orderId,"状态: 等待一级审批负责人进行审批");
}
}
return new AuthInfo("0001","单号: "+orderId,"申请人:"+ name +", 状态: 审批完成!");
}
}
/*******************************************************
* 模拟客户端测试
* @author lbf
*
*******************************************************/
public class Test {
public static void main(String[] args) throws ParseException {
AuthController controller = new AuthController();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = sdf.parse("2022-11-12 00:00:00");
//设置申请流程
//三级审核
//1.调用doAuth方法,模拟发送申请人相关信息
AuthInfo info1 = controller.doAuth("研发小周", "100001000010000", date);
System.out.println("当前审核状态: " + info1.getInfo());
/**
* 2.模拟进行审核操作, 虚拟审核人ID: 1000013
* 调用auth() 方法进行审核操作, 就是向Map中添加一个 审核人ID和申请单ID
*/
AuthService.auth("1000013", "100001000010000");
System.out.println("三级负责人审批完成,审批人: 王工");
System.out.println("===========================================================================");
//二级审核
//1.调用doAuth方法,模拟发送申请人相关信息
AuthInfo info2 = controller.doAuth("研发小周", "100001000010000", date);
System.out.println("当前审核状态: " + info2.getInfo());
/**
* 2.模拟进行审核操作, 虚拟审核人ID: 1000012
* 调用auth() 方法进行审核操作, 就是向Map中添加一个 审核人ID和申请单ID
*/
AuthService.auth("1000012", "100001000010000");
System.out.println("二级负责人审批完成,审批人: 张经理");
System.out.println("===========================================================================");
//一级审核
//1.调用doAuth方法,模拟发送申请人相关信息
AuthInfo info3 = controller.doAuth("研发小周", "100001000010000", date);
System.out.println("当前审核状态: " + info3.getInfo());
/**
* 2.模拟进行审核操作, 虚拟审核人ID: 1000012
* 调用auth() 方法进行审核操作, 就是向Map中添加一个 审核人ID和申请单ID
*/
AuthService.auth("1000011", "100001000010000");
System.out.println("一级负责人审批完成,审批人: 罗总");
}
}
2、使用责任链模式优化上边的代码
统一抽象类AuthLink 下 有三个子类,将三个子类的执行通过编排,模拟出一条链路,这个
链路就是业务中的责任链。如下图所示:
示例代码如下:
/*******************************************************
* 抽象审核练类
* @author lbf
*
*******************************************************/
public abstract class AuthLink {
protected Logger logger = LoggerFactory.getLogger(AuthLink.class);
protected SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
protected String levelUserId; //审核人ID
protected String levelUserName; //审核人姓名
protected AuthLink next; //持有下一个处理类的引用
public AuthLink(String levelUserId, String levelUserName) {
this.levelUserId = levelUserId;
this.levelUserName = levelUserName;
}
//获取下一个处理类
public AuthLink getNext() {
return next;
}
//责任链中添加处理类
public AuthLink appendNext(AuthLink next) {
this.next = next;
return this;
}
//抽象审核方法
//审核方法
public abstract AuthInfo doAuth(String uId, String orderId, Date authDate);
}
/*******************************************************
* 一级负责人--具体处理者1
* @author lbf
*
*******************************************************/
public class Level1AuthLink extends AuthLink{
private Date beginDate = f.parse("2020-11-11 00:00:00");
private Date endDate = f.parse("2020-11-31 23:59:59");
public Level1AuthLink(String levelUserId, String levelUserName) throws ParseException {
super(levelUserId, levelUserName);
}
@Override
public AuthInfo doAuth(String uId, String orderId, Date authDate) {
Date date = AuthService.queryAuthInfo(levelUserId, orderId);
if (null == date) {
return new AuthInfo("0001", "单号:", orderId, " 状态:待一级审批负责人 ", levelUserName);
}
AuthLink next = super.getNext();
if (null == next) {
return new AuthInfo("0000", "单号:", orderId, " 状态:一级审批完成", " 时间:", f.format(date), " 审批人:", levelUserName);
}
if (authDate.before(beginDate) || authDate.after(endDate)) {
return new AuthInfo("0000", "单号:", orderId, " 状态:一级审批完成", " 时间:", f.format(date), " 审批人:", levelUserName);
}
return next.doAuth(uId, orderId, authDate);
}
}
/*******************************************************
* 二级负责人--具体处理者2
* @author lbf
*
*******************************************************/
public class Level2AuthLink extends AuthLink{
private Date beginDate = f.parse("2020-11-11 00:00:00");
private Date endDate = f.parse("2020-11-31 23:59:59");
public Level2AuthLink(String levelUserId, String levelUserName) throws ParseException {
super(levelUserId, levelUserName);
}
public AuthInfo doAuth(String uId, String orderId, Date authDate) {
Date date = AuthService.queryAuthInfo(levelUserId, orderId);
if (null == date) {
return new AuthInfo("0001", "单号:", orderId, " 状态:待二级审批负责人 ", levelUserName);
}
AuthLink next = super.getNext();
if (null == next) {
return new AuthInfo("0000", "单号:", orderId, " 状态:二级审批完成", " 时间:", f.format(date), " 审批人:", levelUserName);
}
if (authDate.before(beginDate) || authDate.after(endDate) ) {
return new AuthInfo("0000", "单号:", orderId, " 状态:二级审批完成", " 时间:", f.format(date), " 审批人:", levelUserName);
}
return next.doAuth(uId, orderId, authDate);
}
}
/*******************************************************
* 三级负责人--具体处理者3
* @author lbf
*
*******************************************************/
public class Level3AuthLink extends AuthLink{
public Level3AuthLink(String levelUserId, String levelUserName) {
super(levelUserId, levelUserName);
}
public AuthInfo doAuth(String uId, String orderId, Date authDate) {
Date date = AuthService.queryAuthInfo(levelUserId, orderId);
if (null == date) {
return new AuthInfo("0001", "单号:", orderId, " 状态:待三级审批负责人 ", levelUserName);
}
AuthLink next = super.getNext();
if (null == next) {
return new AuthInfo("0000", "单号:", orderId, " 状态:三级审批完成", " 时间:", f.format(date), " 审批人:", levelUserName);
}
return next.doAuth(uId, orderId, authDate);
}
}
/*******************************************************
* 模拟客户类测试
* @author lbf
*
*******************************************************/
public class Test {
protected static Logger logger = LoggerFactory.getLogger(Test.class);
public static void main(String[] args) throws ParseException {
AuthLink authLink = new Level3AuthLink("1000013", "王工")
.appendNext(new Level2AuthLink("1000012", "张经理"))
.appendNext(new Level1AuthLink("1000011", "段总"));
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date currentDate = f.parse("2020-11-18 23:49:46");
logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("研发牛马", "1000998004813441", currentDate)));
// 模拟三级负责人审批
AuthService.auth("1000013", "1000998004813441");
logger.info("测试结果:{}", "模拟三级负责人审批,王工");
logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("研发牛马", "1000998004813441", currentDate)));
// 模拟二级负责人审批
AuthService.auth("1000012", "1000998004813441");
logger.info("测试结果:{}", "模拟二级负责人审批,张经理");
logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("研发牛马", "1000998004813441", currentDate)));
// 模拟一级负责人审批
AuthService.auth("1000011", "1000998004813441");
logger.info("测试结果:{}", "模拟一级负责人审批,段总");
logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("研发牛马", "1000998004813441", currentDate)));
}
}
当工作流程发生变化,可以动态地改变链内的成员或者修改它们的次序,也可动态地新增
或者删除责任。并且每个类只需要处理自己该处理的工作,不能处理的传递给下一个对象
完成,明确各类的责任范围,符合类的单一职责原则。
四、责任链模式总结
1、责任链模式优点
1)降低了对象之间的耦合度,该模式降低了请求发送者和接收者的耦合度。
2)增强了系统的可扩展性,可以根据需要增加新的请求处理类,满足开闭原则。
3)增强了给对象指派职责的灵活性,当工作流程发生变化,可以动态地改变链内的成员
或者修改它们的次序,也可动态地新增或者删除责任。
4)责任链简化了对象之间的连接,个对象只需保持一个指向其后继者的引用,不需保持
其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
5)责任分担,每个类只需要处理自己该处理的工作,不能处理的传递给下一个对象完成,
明确各类的责任范围,符合类的单一职责原则。
2、责任链模式缺点
1)不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定
会被处理,该请求可能一直传到链的末端都得不到处理。
2)对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
3)职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链
的错误设置而导致系统出错,如可能会造成循环调用。
3、责任链模式适用场景
1)在运行时需要动态使用多个关联对象来处理同一次请求时。比如,请假流程、员工入
职流程、编译打包发布上线流程等。
2)不想让使用者知道具体的处理逻辑时。比如,做权限校验的登录拦截器。
3)需要动态更换处理对象时。比如,工单处理系统、网关 API 过滤规则系统等。
4)职责链模式常被用在框架开发中,用来实现框架的过滤器、拦截器功能,让框架的使用者在不修改源码的情况下,添加新的过滤拦截功能。