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