本文通过图书馆管理系统中,用户名校验、明码校验、须要减少问题,每次都要减少if判断语句,将其改用责任链模式进行链式调用,为了让代码更加的优雅,咱们应用之前学过的建造者模式就代码进行革新。接着咱们会介绍责任链模式在咱们罕用的框架中的使用,最初是责任链模式的优缺点和利用场景。
读者能够拉取残缺代码到本地进行学习,实现代码均测试通过后上传到码云。
一、引出问题
小王给老王打造了一套图书馆管理系统,随着访问量的一直减少,老王要求减少拜访的用户名校验。
小王说这有何难,说着就在用户拜访图书馆之前加了一层判断语句,判断用户名是否非法。过了一段时间后,又给每个用户颁发了一个明码,就须要在用户名校验通过当前校验明码。
小王就筹备在用户名的判断语句后,减少明码的校验语句。老王赶紧拦住了要持续更改代码的小王。如果当前再减少角色校验、权限校验、你筹备写多少个判断语句。
而且你把软件设计准则中的——开闭准则丢到哪里去了。
你能够思考应用一种模式,将所有的校验办法都独立进去一个类,每一个类只负责解决各自的校验逻辑,以后的校验类通过当前传递给下一个校验类进行解决,这样每次减少新的逻辑判断都只须要减少校验类就行了。
就像是一条流水线,每个类负责解决线上的一个环节。
二、责任链模式的概念和应用
实际上,老王提出来的正是行为型设计模式中的——*责任链模式*。
责任链模式正如它的名字一样,将每个职责的步骤串联起来执行,并且一个步骤执行实现之后才可能执行下一个步骤。
从名字能够看出通常责任链模式应用链表来实现。 因而当执行工作的申请发动时,从责任链上第一步开始往下传递,直到最初一个步骤实现。
在责任链模式当中, 客户端只用执行一次流程开始的申请便不再须要参加到流程执行当中,责任链上的流程便可能本人始终往下执行,
客户端同样也并不关怀执行流程细节,从而实现与流程之间的解耦。
责任链模式须要有两个角色:
形象处理器(Handler):处理器形象接口,定义了解决申请的办法和执行下一步解决的处理器。
具体处理器(ConcreteHandler):执行申请的具体实现,先依据申请执行解决逻辑,实现之后将申请交给下一个处理器执行。
基于责任链模式实现图书馆的用户名校验和明码校验。
形象处理器:
/** * @author tcy * @Date 22-08-2022 */public abstract class Handler { private Handler next; public Handler getNext() { return next; } public void setNext(Handler next) { this.next = next; } public abstract void handle(Object request);}
用户名校验处理器:
/** * @author tcy * @Date 23-08-2022 */public class ConcreteHandlerUsername extends Handler{ @Override public void handle(Object request) { //相应的业务逻辑... System.out.println("用户名校验通过. 参数: " + request); //调用链路中下一个节点的解决办法 if (getNext() != null) { getNext().handle(request); } }}
明码校验器:
/** * @author tcy * @Date 23-08-2022 */public class ConcreteHandlerPassword extends Handler{ @Override public void handle(Object request) { //相应的业务逻辑... System.out.println("明码校验通过. 参数: " + request); //调用链路中下一个节点的解决办法 if (getNext() != null){ getNext().handle(request); } }}
客户端调用:
public class Client { //一般模式---------- public static void main(String[] args) { Handler concreteHandler1 = new ConcreteHandlerUsername(); Handler concreteHandler2 = new ConcreteHandlerPassword(); concreteHandler1.setNext(concreteHandler2); concreteHandler1.handle("用户名tcy"); } }
用户名校验通过. 参数: 用户名tcy 明码校验通过. 参数: 用户名tcy
这样咱们就实现了责任链模式,然而这种形式咱们留神到,调用方调用的时候手动将两个处理器set到一起,如果这条链路很长的时候,这样的代码切实是太不优雅了。
将咱们已经学过的设计模式扒进去,看应用哪种模式能让它看起来更优雅一点。
三、责任链模式+建造者模式
咱们看建造型设计模式的文章,看建造者模式中的典型利用中的Lombok。
参考Lombok的 @Builder例子,是不是和咱们这个有着些许类似呢?
咱们在Handle的类中创立一个Builder外部类。
/** * 建造者模式 */public static class Builder{ private Handler head; private Handler tail; public Builder addHanlder(Handler handler){ //head==null示意第一次增加到队列 if (null == head){ head = this.tail = handler; return this; } //原tail节点指向新增加进来的节点 this.tail.setNext(handler); //新增加进来的节点设置为tail节点 this.tail = handler; return this; } public Handler build(){ return this.head; }}
该外部类更像是一个链表构造,定义一个头和尾对象,add办法是向链接的头尾中赋值,build返回头元素不便开始链式调用。咱们对调用方代码进行革新。
//建造者模式---------public static void main(String[] args) { Handler.Builder builder = new Handler.Builder(); builder.addHanlder(new ConcreteHandlerUsername()) .addHanlder(new ConcreteHandlerPassword()); builder.build().handle("用户名tcy");}
这样的实现形式比原形式优雅多了。责任链模式自身是很简略的,如果将责任链模式和建造者模式组合起来应用就没那么容易了解了。
在理论应用中往往不是一个繁多的设计模式,更多的是多种组合模式组成的“四不像”,实际上这并不是一件轻松的事。
四、责任链模式在源码使用
为了加深了解咱们持续深刻责任链模式在Spring中的使用。
Spring Web 中的 HandlerInterceptor
,外面有preHandle()
、postHandle()
、afterCompletion()
三个办法,实现这三个办法能够别离在调用"Controller"办法之前,调用"Controller"办法之后渲染"ModelAndView"之前,以及渲染"ModelAndView"之后执行。
public interface HandlerInterceptor { default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; } default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { } default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { }}
HandlerInterceptor
就是角色中的形象解决者,HandlerExecutionChain相当于上述中的Client,用于调用责任链上的各个环节。
public class HandlerExecutionChain {...@Nullableprivate HandlerInterceptor[] interceptors;private int interceptorIndex = -1;boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = 0; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; if (!interceptor.preHandle(request, response, this.handler)) { triggerAfterCompletion(request, response, null); return false; } this.interceptorIndex = i; } } return true;}void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = interceptors.length - 1; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; interceptor.postHandle(request, response, this.handler, mv); } }}void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = this.interceptorIndex; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; try { interceptor.afterCompletion(request, response, this.handler, ex); } catch (Throwable ex2) { logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); } } }}}
公有的数组 private HandlerInterceptor[] interceptors 用于存储责任链的每个环节,,而后通过interceptorIndex
作为指针去遍历责任链数组按顺序调用解决者。
联合咱们下面给出的例子,在Spring中的利用是比拟容易了解的。
在Servlet的一系列拦截器也是采纳的责任链模式,有趣味的读者能够深入研究一下。
五、总结
当必须按程序执行多个解决者时,能够思考应用责任链模式。如果解决者的程序及其必须在运行时扭转时,能够思考应用责任链模式。责任链的模式是毛病也很显著,减少了零碎的复杂性。
然而要切忌防止适度设计,在理论利用中,校验用户名和明码的业务逻辑并没有那么的简单,可能只是一个判断语句,应用设计模式只会减少零碎的复杂性,而在Shiro、SpringSecurity、SpringMVC的拦截器中应用责任链模式是一个好的抉择。
如果在你的我的项目业务中须要定义一系列拦截器,那么应用责任链模式就是一个比拟不错的抉择。
我曾经间断更新了数十篇设计模式博客,举荐你联合学习。
一、设计模式概述
二、设计模式之工厂办法和形象工厂
三、设计模式之单例和原型
四、设计模式之建造者模式
五、设计模式之代理模式
六、设计模式之适配器模式
七、设计模式之桥接模式
八、设计模式之组合模式
九、设计模式之装璜器模式
十、设计模式之外观模式
十一、外观模式之享元模式