我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Java设计模式之责任链模式

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Java设计模式之责任链模式

本文通过图书馆管理系统中,用户名校验、密码校验、需要增加问题,每次都要增加if判断语句,将其改用责任链模式进行链式调用,为了让代码更加的优雅,我们使用之前学过的建造者模式就代码进行改造。接着我们会介绍责任链模式在我们常用的框架中的运用,最后是责任链模式的优缺点和应用场景。

读者可以拉取完整代码到本地进行学习,实现代码均测试通过后上传到码云,本地源码下载。

一、引出问题

小王给老王打造了一套图书馆管理系统,随着访问量的不断增加,老王要求增加访问的用户名校验。

小王说这有何难,说着就在用户访问图书馆之前加了一层判断语句,判断用户名是否合法。过了一段时间后,又给每个用户颁发了一个密码,就需要在用户名校验通过以后校验密码。

小王就准备在用户名的判断语句后,增加密码的校验语句。老王赶忙拦住了要继续更改代码的小王。如果以后再增加角色校验、权限校验、你准备写多少个判断语句。

而且你把软件设计原则中的——开闭原则丢到哪里去了。

你可以考虑使用一种模式,将所有的校验方法都独立出来一个类,每一个类只负责处理各自的校验逻辑,当前的校验类通过以后传递给下一个校验类进行处理,这样每次增加新的逻辑判断都只需要增加校验类就行了。

就像是一条流水线,每个类负责处理线上的一个环节。

二、责任链模式的概念和使用

实际上,老王提出来的正是行为型设计模式中的——**责任链模式。

责任链模式正如它的名字一样,将每个职责的步骤串联起来执行,并且一个步骤执行完成之后才能够执行下一个步骤。

从名字可以看出通常责任链模式使用链表来完成。 因此当执行任务的请求发起时,从责任链上第一步开始往下传递,直到最后一个步骤完成。

在责任链模式当中, 客户端只用执行一次流程开始的请求便不再需要参与到流程执行当中,责任链上的流程便能够自己一直往下执行,

客户端同样也并不关心执行流程细节,从而实现与流程之间的解耦。

责任链模式需要有两个角色:

抽象处理器(Handler):处理器抽象接口,定义了处理请求的方法和执行下一步处理的处理器。

具体处理器(ConcreteHandler):执行请求的具体实现,先根据请求执行处理逻辑,完成之后将请求交给下一个处理器执行。

基于责任链模式实现图书馆的用户名校验和密码校验。

抽象处理器:


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);


}

用户名校验处理器:


public class ConcreteHandlerUsername extends Handler{
    @Override
    public void handle(Object request) {

        //相应的业务逻辑...
        System.out.println("用户名校验通过. 参数: " + request);

        //调用链路中下一个节点的处理方法
        if (getNext() != null) {

            getNext().handle(request);
        }

    }
}

密码校验器:


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 {
...

@Nullable
private 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的拦截器中使用责任链模式是一个好的选择。

如果在你的项目业务中需要定义一系列拦截器,那么使用责任链模式就是一个比较不错的选择。

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对编程网的支持。如果你想了解更多相关内容请查看下面相关链接

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Java设计模式之责任链模式

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Java设计模式之责任链模式的示例详解

责任链模式是将链中的每一个节点看做是一个对象,每个节点处理的请求均不相同,且内部自动维护下一个节点对象,当一个请求从链式的首段发出时,会沿着链的路径依次传递给每一个节点对象。本文将通过示例和大家详细聊聊责任链模式,需要的可以参考一下
2022-11-13

每天一个设计模式之责任链模式

作者按:《每天一个设计模式》旨在初步领会设计模式的精髓,目前采用javascript和python两种语言实现。诚然,每种设计模式都有多种实现方式,但此小册只记录最直截了当的实现方式 :)0. 项目地址责任链模式·代码《每天一个设计模式》地
2023-01-31

java设计模式中的责任链模式是什么

本篇文章为大家展示了java设计模式中的责任链模式是什么,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。一:模式说明模式定义:使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。
2023-06-22

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录