看懂了责任链模式,你就能明白不少

前言

只有光头才能变强。html

文本已收录至个人GitHub精选文章,欢迎Starhttps://github.com/ZhongFuCheng3y/3yjava

最近在看项目代码的时候发现「责任链模式」,因而想花点时间来写写什么是责任链模式。git

不知道你们是怎么学习设计模式的,通常我都是用到的时候,或者接触到的时候才会去学。不然感受学完就很容易就忘掉了,不能理解为何要使用设计模式(由于没有真实的场景给我去使用)。github

在以前我已经更新说几篇设计模式的文章了,我以为写得「还行」,有兴趣的同窗能够到个人GitHub上,关键字搜索「设计模式」,就能找到对应的文章。设计模式

不得不说,我如今负责项目的代码我经常会感叹:这代码怎么这么骚啊!项目里边用到了不少的设计模式,在最开始看的时候会很费劲(由于以前没学),但维护起来是真的方便。框架

1、什么是责任链模式?

在说责任链模式以前,咱们先来聊聊「过滤器」。ide

过滤器相信你们都确定学过了,在最开始学Servlet的时候咱们会学到Filter。等学到Struts2的时候,咱们会学到Interceptor。等学到SpringMVC的时候,咱们会学到HandlerInterceptor工具

但不管学哪一个框架,咱们发现是最终其实它仍是作Filter这么一件事。说白了就是:学习

  • 把全部的过滤器都放在FilterChain里边,依次执行每一个过滤器。

在个人GitHub对Filter,HandlerInterceptor,Interceptor都有讲到,若是想要复习的同窗不妨进去搜索关键字「过滤器」「Struts2」「SpringMVCui

为何看责任链模式要聊「过滤器」呢?后面会讲到,不要着急。

1.1 干掉敖丙和鸡蛋

举个例子:把咱们的正常请求想象成一堆的杂物,里边有米豆,有鸡蛋,有敖丙公仔玩具等等一些杂物。

如今咱们想要最后获得的是米豆,鸡蛋和敖丙玩具都被过滤掉。因而咱们就能够搞两个滤网,把敖丙玩具和鸡蛋给过滤掉。

以最快的方式,咱们能够写if来把这个需求给搞掂,下面上代码。

一个请求,咱们使用Request对象来表示:

public class Request {
    // 请求的数据
    private String data;

    public String getData() {
        return data;
    }
    public void setData(String data) {
        this.data = data;
    }
}

针对请求,咱们确定是有一个接口处理请求的啦,咱们使用Handler来表示:

public class Handler {

    public void handlerRequest(Request request) {

        // 获得请求的数据
        String data = request.getData();

        if (data.contains("鸡蛋")) {
            filterEgg(data);
        }
        if (data.contains("敖丙工具")) {
            filterAoBing(data);
        }
        // 我到这里就能拿到米豆了。
    }

    private void filterAoBing(String data) {
        //doSomething
    }

    private void filterEgg(String data) {
        //doSomething
    }
}

上面的代码你们不知道熟不熟悉,反正我就很熟悉,不少时候我就是这样写代码的(在现实里边不少代码就是这样的)。

1.2 如何更加优雅干掉敖丙和鸡蛋?

在某年某月产品过来告诉我,须要新增一种类型想要过滤的「白菜」

在某年某月产品过来告诉我,须要新增一种类型想要过滤的「鸡腿」

在某年某月产品过来告诉我,须要新增一种类型想要过滤的「鸡头」

因而咱们的Handler处理就可能「膨胀」起来了,多是这样?

public class Handler {

    public void handlerRequest(Request request) {

        // 获得请求的数据
        String data = request.getData();

        if (data.contains("鸡蛋")) {
            filterEgg(data);
        }
        if (data.contains("敖丙工具")) {
            filterAoBing(data);
        }
        if (data.contains("白菜")) {
            filterBaiCai(data);
        }
        if (data.contains("鸡头")) {
            filterJiTou(data);
        }
        if (data.contains("鸡腿")) {
            filterJiTui(data);
        }
        // 我到这里就能拿到米豆了。
    }

    private void filterJiTou(String data) {
        //doSomething
    }

    private void filterJiTui(String data) {
        //doSomething
    }

    private void filterAoBing(String data) {
        //doSomething
    }

    private void filterEgg(String data) {
        //doSomething
    }
}

明显的是,若是处理的流程改动比较大的话(须要增删改其中的某个流程),那我每次都须要更改handlerRequest的代码,增长/修改/删除一个if和一个处理方法。

更加面向对象的方式是这样的:将每一个处理的方式抽象成一个类,每一个类各司其职

不管是过滤敖丙仍是过滤鸡蛋仍是过滤米豆,作的事都是过滤。咱们就能够将其抽象成接口。因而咱们就有一个接口,多个实现类

public interface Filter {
    // 过滤
    void doFilter(String data);
}

class FilterEgg implements Filter {

    @Override
    public void doFilter(String data) {
        //doSomething
    }
}

class FilterAoBing implements Filter {

    @Override
    public void doFilter(String data) {
        //doSomething
    }
}

class FilterBaiCai implements Filter {

    @Override
    public void doFilter(String data) {
        //doSomething
    }
}

class FilterJiTou implements Filter {

    @Override
    public void doFilter(String data) {
        //doSomething
    }
}

每一个各司其职的Filter都有可能被执行,咱们能够将其串成一条链,抽象一层对外只暴露一个方法来替代if。因而咱们能够写出一个FilterChain

public class FilterChain {
    List<Filter> filters = new ArrayList<>();

    public FilterChain() {
        filters.add(new FilterEgg());
        filters.add(new FilterAoBing());
        filters.add(new FilterBaiCai());
        filters.add(new FilterJiTou());
    }
    public void processData(String data) {
        for (Filter filter : filters) {
            filter.doFilter(data);
        }
    }
}

改造事后,咱们的Handler就长这个样子了:

public class Handler {
    public void handlerRequest(Request request) {
        // 获得请求的数据
        String data = request.getData();
        FilterChain filterChain = new FilterChain();
        // 处理数据
        filterChain.processData(data);
    }
}

若是我告诉你,这种的处理方式就是责任链模式,你会怎么想?

2、为何责任链模式?

再来回顾一下,我作了些什么:

  1. 将处理的各个流程抽象为各个类(原本Handler里边有多个if方法)
  2. 将多个类用Chain链起来,暴露一个方法给Handler使用
  3. done

下面我画了一张对比图:

是否是很简单?说到底仍是抽象了一层(将每一个处理抽象为一个类而已)。

那为何要这样干?若是我要增长一个处理流程,我是得新增一个处理类,而后在链上增长相对应的类。操做也的确如此。

这不麻烦吗?要便捷的话,我还不如直接增长一个if,一个处理方法来得方便呢。

用责任链模式的好处就是分工明确,解耦,容易维护

  • 将多个条件断定分散到各个的处理类上,相对于if else耦合性相对较低。
  • 增长一个具体的Handler处理类,不会影响到BaseHandler的代码

责任链模式的缺点:

  • 项目里边会有多个具体Handler类(由于每种处理都抽象为一个类,因此会有多个类)
  • 很差调试,初看代码时很差阅读。(对外只是一个doChain方法,而里边由多个处理类来组成,还得看相应的调用顺序)

3、再来聊聊责任链模式

咱们从上面也能够看到责任链模式主要有如下特色:

  • 一个Handler接口,多个Handler处理类
  • 多个Handler处理类串起来造成一条链

有这两个特色我就称这些代码运用了责任链模式。在翻阅资料或者看书的时候,你可能会看到:“责任链和不纯责任链”

  • 纯:请求执行到某个具体的Handler,该Handler要么自行处理而后结束请求,要么不处理继续往下给别的Handler执行。

  • 不纯:请求执行到某个具体的Handler,该Handler自行处理了,继续往下给别的Handler执行。

还有就是将各个具体的Handler串成一条链,这里边的实现会有各式各样的:

  • 在我例子里是直接new出一个ArrayList,而后在构造方法里边代码手动add到ArrayList的
  • 有可能会在代码里边每一个具体Handler都会记录本身下一个Handler是谁
  • 有可能将Handler的初始化放在XML上
  • ….//反正各类操做最终仍是会将各个Handler串起来

其实没必要要在乎纯和不纯的责任链模式,咱们学设计模式是为了学它的思想

4、看看JavaWeb的Filter

在文章最开头我就说了咱们之前学过的Filter,其实Filter就是用了责任链模式。咱们来简单看看代码:

咱们在使用Filter过滤器的时候,要么在XML上配置<filter>,要么在代码上写上注解@WebFilter(filterName = "",urlPatterns = "")

这些配置都会在Web容器启动的时候被读取,读完这些配置,会将你写的Filter过滤器加到FilterChain里边:

咱们能够看到Filter接口下有不少都实现了doFilter

JavaWeb的Filter实际用到的也是责任链模式。

最后

设计模式自己不是一件很复杂的东西,像门面模式,模板方法模式都很是容易理解。学完了会有一种感受:“啊?就这?

重要的是学完能不能用到实际的工做中,这是很是难能难得的。咱们写代码按照自身的思惟写if else是很是简单的,而设计模式每每须要绕一个圈才能把功能实现。

可是,合理运用设计模式的代码是很是好维护的。若是你懂设计模式,那代码会看起来很是清晰。若是你不懂设计模式,你就会感叹“这代码是真的骚阿”(这就是我…)。

好好学习,但愿有朝一日,别人看到个人代码,在背后说「这人写的代码是真的骚,牛逼阿」。

参考资料:

  • 《设计模式之禅》
  • https://www.cnblogs.com/tanshaoshenghao/p/10741160.html

本已收录至个人GitHub精选文章,欢迎Starhttps://github.com/ZhongFuCheng3y/3y

乐于输出干货的Java技术公众号:Java3y。公众号内有300多篇原创技术文章、海量视频资源、精美脑图,关注便可获取!

很是感谢人才们能看到这里,若是这个文章写得还不错,以为「三歪」我有点东西的话 求点赞 求关注️ 求分享👥 求留言💬 对暖男我来讲真的 很是有用!!!

创做不易,各位的支持和承认,就是我创做的最大动力,咱们下篇文章见!

相关文章
相关标签/搜索