我们在进行软件开发时要想实现可维护、可扩展,就需要尽量复用代码,并且降低代码的耦合度。设计模式就是一种可以提高代码可复用性、可维护性、可扩展性以及可读性的解决方案。大家熟知的23种设计模式,可以分为创建型模式、结构型模式和行为型模式三大类。本专题着眼于实际开发过程中常用的几种设计模式,从理论和实战两个角度进行讨论和分享,力求逻辑清晰、表述简洁,帮助大家在项目中合理运用设计模式,保障代码的可靠性。
第一篇:浅析设计模式1——工厂模式
第二篇:浅析设计模式2——策略模式
第三篇:浅析设计模式3——装饰者模式
第四篇:浅析设计模式4——模板方法模式
概述
大家熟知的23种设计模式,可以分为创建型模式、结构型模式和行为型模式三大类。其中,行为型模式可用于描述程序中多个类和多个对象如何协作完成复杂的任务,涉及不同对象间的职责分配、算法的抽象化。责任链模式是一种典型的行为型模式,本文将着眼于责任链模式进行学习分享,阐述责任链模式的核心概念和应用实践。认识和使用责任链模式,可以在开发中有效增强应用的灵活性和可扩展性。
基本概念
下面从模式结构和使用步骤两个方面,简单阐述责任链方法模式的基本概念。
▐结构
|
|
|
|
|
|
|
|
|
|
|
|
使用示例
其实,大家平常接触到的各种审批流,就涉及多个审批节点,不同的审批节点负责人分别持有不同级别的审批权限。比如:请假审批流、紧急发布审批流、报销审批流等等。疫情管控时期,我们学校为了确保在校师生及其他员工的安全,会严格要求学生不能随意出入校门,如果有特殊情况如看病就医,则需在校务系统中进行申请,系统会根据学生请假出校的时长,设置不同级别的负责人进行严格审批。这一过程,用责任链模式就可以实现。
▐代码实现
// 创建抽象处理者类,定义指向下一节点的指针和抽象处理方法 public abstract class AbstractHandler { private AbstractHandler next; public void setNext(AbstractHandler next) { this.next = next; } public AbstractHandler getNext() { return next; } public abstract void handleRequest(int leaveDayNum); } // 定义具体处理者类1:系统自动审批 public class ConcreteHandler1 extends AbstractHandler { @Override public void handleRequest(int leaveDayNum) { if (leaveDayNum <= 1) { System.out.println("请假不超过" + leaveDayNum + "天" + ": 学校自动审批通过"); } else { if (getNext() != null) { getNext().handleRequest(leaveDayNum); } else { System.out.println("请假天数过长,需向学院提供签字承诺书"); } } } } // 定义具体处理中类2:导师审批 public class ConcreteHandler2 extends AbstractHandler { @Override public void handleRequest(int leaveDayNum) { if (leaveDayNum <= 3) { System.out.println("请假不超过" + leaveDayNum + "天" + ": 导师审批通过"); } else { if (getNext() != null) { getNext().handleRequest(leaveDayNum); } else { System.out.println("请假天数过长,需向学院提供签字承诺书"); } } } } // 定义具体处理者类3:辅导员审批 public class ConcreteHandler3 extends AbstractHandler { @Override public void handleRequest(int leaveDayNum) { if (leaveDayNum <= 5) { System.out.println("请假不超过" + leaveDayNum + "天" + ": 辅导员审批通过"); } else { if (getNext() != null) { getNext().handleRequest(leaveDayNum); } else { System.out.println("请假天数过长,需向学院提供签字承诺书"); } } } } // 定义具体处理者类4:院长审批 public class ConcreteHandler4 extends AbstractHandler { @Override public void handleRequest(int leaveDayNum) { if (leaveDayNum <= 7) { System.out.println("请假不超过" + leaveDayNum + "天" + ": 院长审批通过"); } else { if (getNext() != null) { getNext().handleRequest(leaveDayNum); } else { System.out.println("请假天数过长,需向学院提供签字承诺书"); } } } } //定义客户类:组装责任链,向链头节点发送请求 public class chainOfResponsibility { public static void main(String[] args) { AbstractHandler handler1 = new ConcreteHandler1(); AbstractHandler handler2 = new ConcreteHandler2(); AbstractHandler handler3 = new ConcreteHandler3(); AbstractHandler handler4 = new ConcreteHandler4(); handler1.setNext(handler2); handler2.setNext(handler3); handler3.setNext(handler4); handler1.handleRequest(6); } }
请假不超过6天: 院长审批通过
扩展
很多文章在介绍责任链模式时都会提到一个概念:纯的责任链模式、不纯的责任链模式,这里也做一个简单的扩展。
- 纯的职责链模式:一个请求必须被某一个处理者对象所接收,且一个具体处理者对某个请求的处理只能采用以下两种行为之一:自己处理(承担责任);把责任推给下一个具体处理者处理。
- 不纯的职责链模式:允许出现某一个具体处理者在承担了请求的部分责任后,再将剩余责任传给下一个具体处理者继续处理剩余部分责任。
另外,在日常使用中,Handler 不是一定要提供一个设置后继处理器的接口,可以把维护链路的职责独立出来,如下图所示。
在这种责任链模式中,HandlerChain 来维护整条链路,它提供了增删处理者的方法,并实现了抽象处理者类的接口,负责在责任链中传递请求 Request。同时,HandlerChain 内部维护了当前具体处理者在整个责任链中的索引。当客户类发出请求后,HandlerChain 会驱动第一个具体处理者(pos = 0),并根据需要判断是否继续向后传递,若需要则调用 handle() 方法再次驱动下一个具体处理者(pos 在上一次驱动时自增),若不需要则跳出链路;如此反复,直到有具体处理者判断不需再向后传递,或已经执行到最后一个具体处理者。
源码赏析
在 tomcat 中,每一个 Filter 都是一个具体处理者,不仅能处理请求、还能处理响应。对于 request 来说,责任链结构为 Filter1 -> Filter2 -> Filter3;而对于响应来说,责任链结构为 Filter3 -> Filter2 -> Filter1。这种双向处理的思想其实也很经典,下面将简单分析一下源码。
public interface Filter { public default void init(FilterConfig filterConfig) throws ServletException {} public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException; public default void destroy() {} }
Filter 为抽象处理者,提供了三个方法,其中 doFilter() 方法为处理方法,三个参数分别为请求、响应和链路管理器。接下来看一下链路管理器 FilterChain 的源码。
// FilterChain public interface FilterChain { public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException; } // ApplicationFilterChain public final class ApplicationFilterChain implements FilterChain { // 处理器链路,ApplicationFilterConfig 可以理解为对 Filter 的包装 private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0]; // 当前处理器在链路中的索引 private int pos = 0; // 调用入口 @Override public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { if( Globals.IS_SECURITY_ENABLED ) { final ServletRequest req = request; final ServletResponse res = response; try { java.security.AccessController.doPrivileged( new java.security.PrivilegedExceptionAction<Void>() { @Override public Void run() throws ServletException, IOException { // here internalDoFilter(req,res); return null; } } ); } catch( PrivilegedActionException pe) { Exception e = pe.getException(); if (e instanceof ServletException) throw (ServletException) e; else if (e instanceof IOException) throw (IOException) e; else if (e instanceof RuntimeException) throw (RuntimeException) e; else throw new ServletException(e.getMessage(), e); } } else { // here internalDoFilter(request,response); } } // 开始处理 private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { // 不停的驱动下一个过滤器 if (pos < n) { ApplicationFilterConfig filterConfig = filters[pos++]; try { Filter filter = filterConfig.getFilter(); if (request.isAsyncSupported() && "false".equalsIgnoreCase( filterConfig.getFilterDef().getAsyncSupported())) { request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE); } if( Globals.IS_SECURITY_ENABLED ) { final ServletRequest req = request; final ServletResponse res = response; Principal principal = ((HttpServletRequest) req).getUserPrincipal(); Object[] args = new Object[]{req, res, this}; SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal); } else { // 驱动过滤器 filter.doFilter(request, response, this); } } catch (IOException | ServletException | RuntimeException e) { throw e; } catch (Throwable e) { e = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(e); throw new ServletException(sm.getString("filterChain.filter"), e); } return; } // 链路中没有更多过滤器了,开始进入 servlet try { if (ApplicationDispatcher.WRAP_SAME_OBJECT) { lastServicedRequest.set(request); lastServicedResponse.set(response); } if (request.isAsyncSupported() && !servletSupportsAsync) { request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE); } if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse) && Globals.IS_SECURITY_ENABLED ) { final ServletRequest req = request; final ServletResponse res = response; Principal principal = ((HttpServletRequest) req).getUserPrincipal(); Object[] args = new Object[]{req, res}; SecurityUtil.doAsPrivilege("service", servlet, classTypeUsedInService, args, principal); } else { // 进入servlet 处理实际业务 servlet.service(request, response); } } catch (IOException | ServletException | RuntimeException e) { throw e; } catch (Throwable e) { e = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(e); throw new ServletException(sm.getString("filterChain.servlet"), e); } finally { if (ApplicationDispatcher.WRAP_SAME_OBJECT) { lastServicedRequest.set(null); lastServicedResponse.set(null); } } } }
ApplicationFilterChain 用数组来组织各处理者的先后顺序,并提供一个当前处理者在链路中的索引 pos 。当链路未在中途断开且当前处理者已是最后一个处理者时,调用 Servlet.service(request, response) 进入业务处理逻辑。作为用户,我们可以通过配置文件或者注入等方式,根据需要定义新的 Filter。
优缺点及适用场景
-
降低对象之间的耦合度。一个节点对象无须关心链的结构、到底是哪一个对象处理其请求,发送者和接收者也无须拥有对方的明确信息。 -
增强系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。 -
灵活地为对象指派职责。当工作流程变化时,可动态改变节点或调动节点次序,也可动态增删节点。 -
简化节点之间的连接。各节点只需保持一个指向其后继者的引用,避免使用众多 if 或 if…else 语句。 -
责任分担,符合类的单一职责原则。每个节点类只需处理自己该处理的工作,不该处理的传递给下一个节点类完成,各类的责任范围非常明确。
- 不能保证每个请求一定被处理。一个请求没有明确的接收者,可能一直传到链的末端都得不到处理。
- 当责任链太长时,一个请求可能需要涉及多个处理者,系统性能会受到一定影响。
- 责任链建立的合理性需要由客户端来保证,增加了客户端的复杂性,也可能会因为错误设置而导致系统陷入死循环。
- 在运行时需要动态使用多个关联对象来处理同一次请求时。比如,请假流程、员工入职流程、编译打包发布上线流程等。
- 不想让使用者知道具体的处理逻辑时。比如,做权限校验的登录拦截器。
- 需要动态更换处理对象时。比如,工单处理系统、网关 API 过滤规则系统等。
- 职责链模式常被用在框架开发中,实现过滤器、拦截器等功能,使用者可以在不修改源码的情况下,添加新的过滤拦截功能。
本文仅供学习!所有权归属原作者。侵删!文章来源: 大淘宝技术
文章评论