责任链模式,更灵活的if-else
责任链模式
责任链,顾名思义,就是用来处理相关事务责任的一条执行链,执行链上有多个节点,每个节点都有机会(条件匹配)处理请求事务,如果某个节点处理完了就可以根据实际业务需求传递给下一个节点继续处理或者返回处理完毕。
这种模式给予请求的类型,对请求的发送者和接收者进行解耦。属于行为型模式。
在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
先来看一段代码
public void test(int i, Request request){ if(i==1){ Handler1.response(request); }else if(i == 2){ Handler2.response(request); }else if(i == 3){ Handler3.response(request); }else if(i == 4){ Handler4.response(request); }else{ Handler5.response(request); } }
代码的业务逻辑是这样的,方法有两个参数:整数 i 和一个请求 request,根据 i 的值来决定由谁来处理 request,如果 i==1,由 Handler1来处理,如果 i==2,由 Handler2 来处理,以此类推。在编程中,这种处理业务的方法非常常见,所有处理请求的类由if…else…条件判断语句连成一条责任链来对请求进行处理,相信大家都经常用到。这种方法的优点是非常直观,简单明了,并且比较容易维护,但是这种方法也存在着几个比较令人头疼的问题:
- 代码臃肿:实际应用中的判定条件通常不是这么简单地判断是否为1或者是否为2,也许需要复杂的计算,也许需要查询数据库等等,这就会有很多额外的代码,如果判断条件再比较多,那么这个if…else…语句基本上就没法看了。
- 耦合度高:如果我们想继续添加处理请求的类,那么就要继续添加if…else…判定条件;另外,这个条件判定的顺序也是写死的,如果想改变顺序,那么也只能修改这个条件语句。
既然缺点我们已经清楚了,就要想办法来解决。这个场景的业务逻辑很简单:如果满足条件1,则由 Handler1 来处理,不满足则向下传递;如果满足条件2,则由 Handler2 来处理,不满足则继续向下传递,以此类推,直到条件结束。其实改进的方法也很简单,就是把判定条件的部分放到处理类中,这就是责任连模式的原理。
定义
责任链模式(Chain of Responsibility Pattern):使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
角色
- Handler: 抽象处理类,抽象处理类中主要包含一个指向下一处理类的成员变量nextHandler和一个处理请求的方法handRequest,handRequest方法的主要主要思想是,如果满足处理的条件,则有本处理类来进行处理,否则由nextHandler来处理
- ConcreteHandler: 具体处理类主要是对具体的处理逻辑和处理的适用条件进行实现。具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。由于具体处理者持有对下家的引用,因此,如果需要,具体处理者可以访问下家
- Client:客户端
类图
coding
public abstract class Handler { private Handler nextHandler; private int level; public Handler(int level) { this.level = level; } public void setNextHandler(Handler handler){ this.nextHandler = handler; } public final void handlerRequest(Request request){ if(level == request.getLevel()){ this.response(request); }else{ if (this.nextHandler != null){ this.nextHandler.handlerRequest(request); }else{ System.out.println("===已经没有处理器了==="); } } } // 抽象方法,子类实现 public abstract void response(Request request); } class Request { int level = 0; public Request(int level){ this.level = level; } public int getLevel() { return level; } }
public class ConcreteHandler1 extends Handler { public ConcreteHandler1(int level) { super(level); } @Override public void response(Request request) { System.out.println("请求由处理器1进行处理"); } } public class ConcreteHandler2 extends Handler { //... } public class ConcreteHandler2 extends Handler { //... }
public class Client { public static void main(String[] args) { ConcreteHandler1 handler1 = new ConcreteHandler1(1); ConcreteHandler2 handler2 = new ConcreteHandler2(2); ConcreteHandler3 handler3 = new ConcreteHandler3(3); //处理者构成一个环形 handler1.setNextHandler(handler2); handler2.setNextHandler(handler3); handler1.handlerRequest(new Request(1)); } }
实例
当你想要让一个以上的对象有机会能够处理某个请求的时候,就是用责任链模式。
通过责任链模式,你可以为某个请求创建一个对象链。每个对象依序检查此请求,并对其进行处理,或者将它传给链中的下一个对象。
比如
- 程序员要请3天以上的假期,在OA申请,需要直接主管、总监、HR 层层审批后才生效。类似的采购审批、报销审批。。。
- 美团在外卖营销业务中资源位展示的逻辑 https://tech.meituan.com/2020/03/19/design-pattern-practice-in-marketing.html
应用
JAVA 中的异常处理机制、JAVA WEB 中 Apache Tomcat 对 Encoding 的处理,Struts2 的拦截器,JSP、Servlet 的 Filter 均是责任链的典型应用。
Servlet 中的责任链
public final class ApplicationFilterChain implements FilterChain { private static final ThreadLocal<ServletRequest> lastServicedRequest; private static final ThreadLocal<ServletResponse> lastServicedResponse; public static final int INCREMENT = 10; private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0]; private int pos = 0; //下一个要执行的filter的位置 private int n = 0; //filter个数 private Servlet servlet = null; public ApplicationFilterChain() { } public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { if (Globals.IS_SECURITY_ENABLED) { final ServletRequest req = request; final ServletResponse res = response; try { AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() { public Void run() throws ServletException, IOException { ApplicationFilterChain.this.internalDoFilter(req, res); return null; } }); } catch (PrivilegedActionException var7) { Exception e = var7.getException(); if (e instanceof ServletException) { throw (ServletException)e; } if (e instanceof IOException) { throw (IOException)e; } if (e instanceof RuntimeException) { throw (RuntimeException)e; } throw new ServletException(e.getMessage(), e); } } else { this.internalDoFilter(request, response); } }
FilterChain 就是一条过滤链。其中每个过滤器(Filter)都可以决定是否执行下一步。过滤分两个方向,进和出:
进:在把ServletRequest和ServletResponse交给Servlet的service方法之前,需要进行过滤
出:在service方法完成后,往客户端发送之前,需要进行过滤
Spring MVC 中的责任链
Spring MVC 的 diapatcherServlet 的 doDispatch 方法中,获取与请求匹配的处理器 HandlerExecutionChain
就是用到了责任链模式。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; //使用到了责任链模式 boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { try { ModelAndView mv = null; Object dispatchException = null; try { processedRequest = this.checkMultipart(request); multipartRequestParsed = processedRequest != request; mappedHandler = this.getHandler(processedRequest); if (mappedHandler == null) { this.noHandlerFound(processedRequest, response); return; } HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler()); String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) { return; } } //责任链模式执行预处理方法,其实是将请求交给注册的拦截器执行 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } this.applyDefaultViewName(processedRequest, mv); //责任链执行后处理方法 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception var22) { //... } finally { } }
- SpringMVC 请求的流程中,执行了拦截器相关方法 interceptor.preHandler 等等
- 在处理 SpringMVC 请求时,使用到职责链模式还使用到适配器模式
- HandlerExecutionChain 主要负责的是请求拦截器的执行和请求处理,但是他本身不处理请求,只是将请求分配给链上注册处理器执行,这是职责链实现方式,减少职责链本身与处理逻辑之间的耦合,规范了处理流程
- HandlerExecutionChain 维护了 HandlerInterceptor 的集合, 可以向其中注册相应的拦截器
总结
责任链模式其实就是一个灵活版的 if…else…语句,它就是将这些判定条件的语句放到了各个处理类中,这样做的优点是比较灵活了,但同样也带来了风险,比如设置处理类前后关系时,一定要特别仔细,搞对处理类前后逻辑的条件判断关系,并且注意不要在链中出现循环引用的问题。
优点:
- 降低耦合度:将请求和处理分开,实现解耦,提高了系统的灵活性。
- 简化了对象:对象不需要知道链的结构
- 良好的扩展性:增加处理者的实现很简单,只需重写处理请求业务逻辑的方法。
缺点:
- 从链头发出,直到有处理者响应,在责任链比较长的时候会影响系统性能,一般需要在 Handler 中设置一个最大节点数。
- 请求递归,调试排错比较麻烦。
使用场景:
- 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
- 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
- 可动态指定一组对象处理请求。
模式的扩展:
职责链模式存在以下两种情况。
- 纯的职责链模式:一个请求必须被某一个处理者对象所接收,且一个具体处理者对某个请求的处理只能采用以下两种行为之一:自己处理(承担责任);把责任推给下家处理。
- 不纯的职责链模式:允许出现某一个具体处理者对象在承担了请求的一部分责任后又将剩余的责任传给下家的情况,且一个请求可以最终不被任何接收端对象所接收。
参考
《研磨设计模式》
https://wiki.jikexueyuan.com/project/java-design-pattern/chain-responsibility-pattern.html
https://refactoringguru.cn/design-patterns/chain-of-responsibility
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
深度解析Java中的5个“黑魔法”
深度解析Java中的5个“黑魔法” 现在的编程语言越来越复杂,尽管有大量的文档和书籍,这些学习资料仍然只能描述编程语言的冰山一角。而这些编程语言中的很多功能,可能被永远隐藏在黑暗角落。本文将为你解释其中5个Java中隐藏的秘密,可以称其为Java的“黑魔法”。对于这些魔法,会描述它们的实现原理,并结合一些应用场景给出实现代码。 一石二鸟:实现注释(Annotation) 从JDK5开始,Java开始引入注释功能,从此,注释已成为许多Java应用程序和框架的重要组成部分。 在绝大多数情况下,注释将被用于描述语言结构,例如类,字段,方法等,但是在另一种情况下,可以将注释作为可实现的接口。 在常规的使用方法中,注释就是注释,接口就是接口。例如,下面的代码为接口MyInterface添加了一个注释。 @Deprecatedinterface MyInterface {}而接口也只能起到接口的作用,如下面的代码,Person实现了IPerson接口,并实现了getName方法。 interface IPerson { public String getName(); }class Person ...
- 下一篇
Java 的这些坑,你踩到了吗?
云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! 阿里妹导读:日常开发中总会遇到各种各样的“坑”,如何提前规避呢?本文将分享 Java 开发中容易遇到的一些坑,并给出详细的问题解析和避坑方法。 前言 中国有句老话叫"事不过三",指一个人犯了同样的错误,一次两次还可以原谅,再多就不可原谅了。写代码也是如此,同一个代码“坑”,踩第一次叫"长了经验",踩第二次叫"加深印象",踩第三次叫"不长记性",踩三次以上就叫"不可救药"。在本文中,笔者总结了一些 Java 坑,描述了问题现象,进行了问题分析,给出了避坑方法。希望大家在日常工作中,遇到了这类 Java 坑,能够提前避让开来。 1 对象比较方法 JDK 1.7 提供的 Objects.equals 方法,非常方便地实现了对象的比较,有效地避免了繁琐的空指针检查。 问题现象 在 JDK1.7 之前,在判断一个短整型、整型、长整型包装数据类型与常量是否相等时,我们一般这样写: Short shortValue = (short)12345; System.out.println(shortVal...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS7设置SWAP分区,小内存服务器的救世主
- Docker快速安装Oracle11G,搭建oracle11g学习环境