Chain of Responsibility
责任链模式是一种行为设计模式, 允许你将请求沿着处理者链进行发送。 收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。
责任链模式在核心 Java 程序库中的一些示例:
识别方法: 该模式可通过一组对象的行为方法间接调用其他对象的相同方法来识别, 而且所有对象都会遵循相同的接口。
责任链模式结构
样例
模拟新系统上限审批流程场景
而这审批的过程在随着特定时间点会增加不同级别的负责人加入,每个人就像责任链模式中的每一个核心点。对于研发小伙伴并不需要关心具体的审批流程处理细节,只需要知道这个上线更严格,级别也更高,但对于研发人员来说同样是点击相同的提审按钮,等待审核。
模拟审核服务
package behavioral.chainofresponsibility;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 模拟审核服务
*/
public class AuthService {
private static Map<String, Date> authMap = new ConcurrentHashMap<>();
/**
* 查询审核结果
* @param uId
* @param orderId
* @return
*/
public static Date queryAuthInfo(String uId, String orderId) {
return authMap.get(uId.concat(orderId));
}
/**
* 处理审核
* @param uId
* @param orderId
*/
public static void auth(String uId, String orderId) {
authMap.put(uId.concat(orderId), new Date());
}
}
责任链中返回对象定义
package behavioral.chainofresponsibility;
public class AuthInfo {
private String code;
private String info = "";
public AuthInfo(String code, String... infos) {
this.code = code;
for (String str : infos) {
this.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;
}
}
链路抽象类定义
package behavioral.chainofresponsibility;
import behavioral.chainofresponsibility.AuthInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.SimpleDateFormat;
import java.util.Date;
public abstract class AuthLink {
protected Logger logger = LoggerFactory.getLogger(AuthLink.class);
protected SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
protected String levelUserId; //级别人员ID
protected String levelUserName; //级别人员名字
private AuthLink next; //责任链
public AuthLink(String levelUserId, String levelUserName) {
this.levelUserId = levelUserId;
this.levelUserName = levelUserName;
}
public AuthLink next() {
return next;
}
public AuthLink appendNext(AuthLink next) {
this.next = next;
return this;
}
public abstract AuthInfo doAuth(String uId, String orderId, Date authDate);
}
AuthLink next
重点在于可以通过 next
方式获取下一个链路需要处理的节点
三个审核实现类
package behavioral.chainofresponsibility;
import behavioral.chainofresponsibility.AuthInfo;
import behavioral.chainofresponsibility.AuthService;
import java.util.Date;
public class Level1AuthLink extends AuthLink {
public Level1AuthLink(String levelUserId, String levelUserName) {
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.next();
if (null == next) {
return new AuthInfo("0000", "单号: ", orderId, " 状态:一级审批完成", " 时间: ", format.format(date), " 审批人: ", levelUserName);
}
return next.doAuth(uId, orderId, authDate);
}
}
package behavioral.chainofresponsibility;
import behavioral.chainofresponsibility.AuthInfo;
import behavioral.chainofresponsibility.AuthService;
import java.text.ParseException;
import java.util.Date;
public class Level2AuthLink extends AuthLink {
private Date beginDate = format.parse("2021-01-20 00:00:00");
private Date endDate = format.parse("2021-02-20 00:00:00");
public Level2AuthLink(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.next();
if (null == next) {
return new AuthInfo("0000", "单号: ", orderId, " 状态:二级审批完成", " 时间: ", format.format(date), " 审批人: ", levelUserName);
}
if(authDate.before(beginDate) || authDate.after(endDate)){
return new AuthInfo("0000", "单号: ", orderId, " 状态:二级审批完成", " 时间: ", format.format(date), " 审批人: ", levelUserName);
}
return next.doAuth(uId, orderId, authDate);
}
}
package behavioral.chainofresponsibility;
import behavioral.chainofresponsibility.AuthInfo;
import behavioral.chainofresponsibility.AuthService;
import java.text.ParseException;
import java.util.Date;
public class Level3AuthLink extends AuthLink {
private Date beginDate = format.parse("2021-01-20 00:00:00");
private Date endDate = format.parse("2021-02-20 00:00:00");
public Level3AuthLink(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.next();
if (null == next) {
return new AuthInfo("0000", "单号: ", orderId, " 状态:三级审批完成", " 时间: ", format.format(date), " 审批人: ", levelUserName);
}
if(authDate.before(beginDate) || authDate.after(endDate)){
return new AuthInfo("0000", "单号: ", orderId, " 状态:三级审批完成", " 时间: ", format.format(date), " 审批人: ", levelUserName);
}
return next.doAuth(uId, orderId, authDate);
}
}
测试
@Test
public void testAuthLink() throws ParseException {
AuthLink authLink = new Level3AuthLink("1000013", "王工")
.appendNext(new Level2AuthLink("1000012", "张经理")
.appendNext(new Level1AuthLink("1000011", "皮总")));
//模拟三级负责人审批
logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("花染梦", "100059878511", new Date())));
logger.info("测试结果:{}", "模拟三级负责人审批,王工");
AuthService.auth("1000013", "100059878511");
//模拟二级负责人审批
logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("花染梦", "100059878511", new Date())));
logger.info("测试结果:{}", "模拟二级负责人审批,张经理");
AuthService.auth("1000012", "100059878511");
//模拟一级负责人审批
logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("花染梦", "100059878511", new Date())));
logger.info("测试结果:{}", "模拟一级负责人审批,皮总");
AuthService.auth("1000011", "100059878511");
logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("花染梦", "100059878511", new Date())));
}
/**
*18:15:33.528 [main] INFO behavioral.TestChainOfRe - 测试结果:{"code":"0001","info":"单号: 100059878511 状态: 待三级负责人审批, 审批人: 王工"}
* 18:15:33.548 [main] INFO behavioral.TestChainOfRe - 测试结果:模拟三级负责人审批,王工
* 18:15:33.548 [main] INFO behavioral.TestChainOfRe - 测试结果:{"code":"0001","info":"单号: 100059878511 状态: 待二级负责人审批, 审批人: 张经理"}
* 18:15:33.548 [main] INFO behavioral.TestChainOfRe - 测试结果:模拟二级负责人审批,张经理
* 18:15:33.549 [main] INFO behavioral.TestChainOfRe - 测试结果:{"code":"0001","info":"单号: 100059878511 状态: 待一级负责人审批, 审批人: 皮总"}
* 18:15:33.549 [main] INFO behavioral.TestChainOfRe - 测试结果:模拟一级负责人审批,皮总
* 18:15:33.549 [main] INFO behavioral.TestChainOfRe - 测试结果:{"code":"0000","info":"单号: 100059878511 状态:一级审批完成 时间: 2021-01-20 18:15:33 审批人: 皮总"}
*/
适用场景
-
当程序需要使用不同方式处理不同种类请求, 而且请求类型和顺序预先未知时, 可以使用责任链模式。
该模式能将多个处理者连接成一条链。 接收到请求后, 它会 “询问” 每个处理者是否能够对其进行处理。 这样所有处理者都有机会来处理请求。
-
当必须按顺序执行多个处理者时, 可以使用该模式。
无论你以何种顺序将处理者连接成一条链, 所有请求都会严格按照顺序通过链上的处理者。
-
如果所需处理者及其顺序必须在运行时进行改变, 可以使用责任链模式。
如果在处理者类中有对引用成员变量的设定方法, 你将能动态地插入和移除处理者, 或者改变其顺序。
实现方式
-
声明处理者接口并描述请求处理方法的签名。
确定客户端如何将请求数据传递给方法。 最灵活的方式是将请求转换为对象, 然后将其以参数的形式传递给处理函数。
-
为了在具体处理者中消除重复的样本代码, 你可以根据处理者接口创建抽象处理者基类。
该类需要有一个成员变量来存储指向链上下个处理者的引用。 你可以将其设置为不可变类。 但如果你打算在运行时对链进行改变, 则需要定义一个设定方法来修改引用成员变量的值。
为了使用方便, 你还可以实现处理方法的默认行为。 如果还有剩余对象, 该方法会将请求传递给下个对象。 具体处理者还能够通过调用父对象的方法来使用这一行为。
-
依次创建具体处理者子类并实现其处理方法。 每个处理者在接收到请求后都必须做出两个决定:
- 是否自行处理这个请求。
- 是否将该请求沿着链进行传递。
-
客户端可以自行组装链, 或者从其他对象处获得预先组装好的链。 在后一种情况下, 你必须实现工厂类以根据配置或环境设置来创建链。
-
客户端可以触发链中的任意处理者, 而不仅仅是第一个。 请求将通过链进行传递, 直至某个处理者拒绝继续传递, 或者请求到达链尾。
-
由于链的动态性, 客户端需要准备好处理以下情况:
- 链中可能只有单个链接。
- 部分请求可能无法到达链尾。
- 其他请求可能直到链尾都未被处理。
责任链模式优点
- 你可以控制请求处理的顺序。
- 单一职责原则。 你可对发起操作和执行操作的类进行解耦。
- 开闭原则。 你可以在不更改现有代码的情况下在程序中新增处理者。
责任链模式缺点
- 部分请求可能未被处理。