- 定义
责任链模式(Chain Of Responsibility Pattern)顾名思义,就是为请求创建一条处理链路,链路上的每个处理器都判断是否可以处理请求,如果不能处理则往后走,依次从链头走到链尾,直到有处理器可以处理请求。 - 类型
2.1 请求只处理一次
每个节点都有机会处理请求,但是请求只要处理成功就结束了。
场景
流程审批、扑克牌
代码示例
原来
java
public class Apply {
public boolean apply(int requireDay) {
if (requireDay <= 1) {
return true;
} else if (requireDay <= 3) {
applyByLeader(requireDay);
} else {
applyByManager(requireDay);
}
}
public boolean applyByLeader(int requireDay) {
//...
}
public boolean applyByManager(int requireDay) {
//...
}
}
改造后
BaseHandler.java
java
public abstract class BaseHandler {
protected BaseHandler successor;
public void setSuccessor(BaseHandler successor) {
this.successor = successor;
}
public abstract boolean apply(int requireDay);
public void print() {
System.out.println(this.getClass().getSimpleName() + " process.");
}
}
AutoHandler.java
java
public class AutoHandler extends BaseHandler {
@Override
public boolean apply(int requireDay) {
super.print();
if (requireDay <= 1) {
return true;
}
return successor.apply(requireDay);
}
}
LeaderHandler.java
java
public class LeaderHandler extends BaseHandler {
@Override
public boolean apply(int requireDay) {
super.print();
if (requireDay <= 3) {
return true;
}
return successor.apply(requireDay);
}
}
ManagerHandler.java
java
public class ManagerHandler extends BaseHandler {
private static final int MAX_DAY = 996;
@Override
public boolean apply(int requireDay) {
super.print();
return requireDay <= MAX_DAY;
}
}
HandlerClient.java
java
public class HandlerClient {
private static final AutoHandler AUTO_HANDLER;
static {
ManagerHandler managerHandler = new ManagerHandler();
LeaderHandler leaderHandler = new LeaderHandler();
leaderHandler.setSuccessor(managerHandler);
AUTO_HANDLER = new AutoHandler();
AUTO_HANDLER.setSuccessor(leaderHandler);
}
public static void main(String[] args) {
AUTO_HANDLER.apply(1);
System.out.println();
AUTO_HANDLER.apply(3);
System.out.println();
AUTO_HANDLER.apply(5);
}
}
输出:
java
AutoHandler process.
AutoHandler process.
LeaderHandler process.
AutoHandler process.
LeaderHandler process.
ManagerHandler process.
2.2 请求处理多次
每个节点都有机会处理请求,节点处理完之后继续往后走,直到链尾。
场景
• 过滤器 / 拦截器
• JavaEE 的 Servlet 规范定义的 Filter
代码示例
请求如果成功通过 process 处理,则进入下一个 process,如果不通过则被过滤掉,这里不再累述代码。
- 项目实践
有个根据配置构造ODPS查询语句的代码,配置片段如下:
json
{
"name": "Document no",
"code": "service_order_code",
"isBasicField": true,
"fromRerating": false,
"classType": "java.lang.String"
}
原来是通过 if-else 来实现的,代码如下所示:
现在要新增非空校验的字段 notNull,现在配置如下:
java
{
"name": "Document no",
"code": "service_order_code",
"isBasicField": true,
"fromRerating": false,
"classType": "java.lang.String",
"notNull": true
}
发现又得往 if-else 里面硬塞分支,有代码洁癖的我怎么能容忍自己写这种代码?最近也从同事那里了解到责任链模式的厉害之处,索性直接给它优化掉,这里我截取下关键代码片段。
首先声明抽象处理类
java
/**
* 责任链抽象处理器
*/
public abstract class AbstractHandler<T, V> {
protected AbstractHandler<T, V> successor;
public void setSuccessor(AbstractHandler<T, V> handler) {
this.successor = handler;
}
/**
* 处理方法
*
* @param context 上下文
* @return R
*/
public abstract String process(Context<T, V> context);
}
各具体处理类安排上
java
/**
* 责任链入口,扩展字段处理
*/
public class FirstExtendFieldHandler extends AbstractHandler<AdjustmentTemplateDTO, String> {
/**
* 处理方法
*
* @param context 上下文
* @return R
*/
@Override
public String process(Context<AdjustmentTemplateDTO, String> context) {
AdjustmentTemplateDTO request = context.getRequest();
if (!request.getBasicField()) {
String tmpField = request.getFromRerating() ? String.format(GET_JSON_OBJECT, NEW_PARAM, PREFIX + request.getCode()) :
String.format(GET_JSON_OBJECT, OLD_PARAM, PREFIX + request.getCode());
context.setResult(tmpField);
}
return successor.process(context);
}
}
/**
* 非空字段处理器
*/
public class SecondNotNullFieldHandler extends AbstractHandler<AdjustmentTemplateDTO, String> {
/**
* 处理方法
*
* @param context 上下文
* @return R
*/
@Override
public String process(Context<AdjustmentTemplateDTO, String> context) {
AdjustmentTemplateDTO request = context.getRequest();
if (!request.getBasicField()) {
return successor.process(context);
}
if (request.getNotNull() == null || !request.getNotNull()) {
String tmpField = (request.getFromRerating() ? NEW : OLD) + request.getCode();
context.setResult(tmpField);
return successor.process(context);
} else {
String oldValue = OLD + request.getCode();
context.setResult(oldValue);
oldValue = successor.process(context);
String newValue = NEW + request.getCode();
context.setResult(newValue);
newValue = successor.process(context);
String finalField = String.format(OdpsConstants.IF, oldValue, newValue, oldValue);
context.setResult(finalField);
return finalField;
}
}
}
/**
* 时间字段处理
*/
public class ThirdTimeFormatHandler extends AbstractHandler<AdjustmentTemplateDTO, String> {
@Override
public String process(Context<AdjustmentTemplateDTO, String> context) {
AdjustmentTemplateDTO request = context.getRequest();
String finalSql = StringUtils.isNotBlank(request.getTimeFormat())
? String.format(TIMESTAMP_FORMAT, context.getResult(), request.getTimeFormat())
: context.getResult();
context.setResult(finalSql);
return finalSql;
}
}
最后是负责初始化责任链的客户端
java
/**
* 责任链客户端
*/
public class HandlerChainClient {
private static final FirstExtendFieldHandler FIRST_HANDLER;
static {
ThirdTimeFormatHandler thirdHandler = new ThirdTimeFormatHandler();
SecondNotNullFieldHandler secondHandler = new SecondNotNullFieldHandler();
secondHandler.setSuccessor(thirdHandler);
FIRST_HANDLER = new FirstExtendFieldHandler();
FIRST_HANDLER.setSuccessor(secondHandler);
}
/**
* 调用责任链进行处理
*
* @param request 请求参数
* @return result
*/
public static String process(AdjustmentTemplateDTO request) {
AdjustmentContext context = new AdjustmentContext();
context.setRequest(request);
return FIRST_HANDLER.process(context);
}
}
最后,业务代码里又臭又长的 if-else 变成了一行代码。
java
HandlerChainClient.process(request);
- 优点
• 解耦。请求发送者无需知道请求在何时、何处以及如何被处理,实现了发送者与处理者的解耦。
• 灵活、可插拔。可以看到想要添加一个处理流程,只需实现BaseHandler,然后注入到对应的位置即可;删除一个流程也是一样,只需要将本节点的位置替换成下一个节点即可,客户端无需感知处理器的变化。
• 代码优雅,责任链相比 if-else 是更加优雅的。 - 缺点
• 类的数量变多了,组链时要注意避免出现环状结构,导致出现死循环。