从零开始写拦截器

作者: 低情商的大仙 | 来源:发表于2018-12-08 10:13 被阅读5次

拦截器的应用是非常广泛的,okhttp中有拦截器,spring框架中也有拦截器,这个年代,你要是不知道什么是拦截器你都不好意思说你是程序员。为了防止大家不好意思出门打招呼,今天我带大家从零开始写一个简单的拦截器。

需求

我们初始需求就是对一个数字乘以10,然后返回结果
这个简单,我们这样处理:

    public int handleNum(int num){
        return num*10;
    }

增加需求1: 如果这个数字是2的倍数,要减去1

public int handleNum(int num){
        if(num % 2 == 0){
            num = num -1;
        }
        return num*10;
    }

增加需求2:在乘10之前,如果数字是99,要减去10

public int handleNum(int num){
        if(num % 2 == 0){
            num = num -1;
        }
        if(num == 99){
            num -= 10;
        }
        return num*10;
    }

随着需求的不断增加,我们就会发现这个handleNum()方法不断的膨胀,而且每一次修改都有可能影响之前的功能,这样的设计是非常不合理的。所以,拦截器这个时候就起到作用了。

基本责任链的拦截器

首先是一个抽象处理类:

public abstract class NumberHandler {
    /**
     * 处理拦截器的方法
     * @param num 待处理的数字
     * @return 处理后的数字
     */
    abstract int handleNum(int num);
}

然后分别是两个需求对应的两个拦截器,首先是为了实现:如果这个数字是2的倍数,要减去1

public class EvenNumHandler extends NumberHandler {
    @Override
    int handleNum(int num) {
       if(num % 2 == 0){
           num -= 1;
       }
        return num;
    }
}

接着是:在乘10之前,如果数字是99,要减去10

public class NinetyNineHnadler extends NumberHandler {
    @Override
    int handleNum(int num) {
        if(num  == 99){
            num -= 10;
        }
        return num;
    }
}

然后是拦截器的调用:

public  int handle(int num){
        List<NumberHandler> handlers = new ArrayList<>();
        handlers.add(new EvenNumHandler());
        handlers.add(new NinetyNineHnadler());
        int res = num;
        for(NumberHandler handler : handlers){
            res = handler.handleNum(res);
        }
        return res * 10;
    }

至此,我们成功实现了一个基本的拦截器,无论对“传入”的数字有什么特殊需求,我们都可以添加一个基本拦截器实现,而且不会影响其他需求,注意,这里核心功能乘以10并不是由拦截器实现的。

增强版拦截器

现在,我们加入一个新的需求:
对乘以10后的数字,如果等于970要减去1
面对这样一个需求,我们发现之前的拦截器有一个缺陷,它只能对输入数据做拦截,确拦截不了返回的数据,也就是乘以10之后的数据,我们看,如果要实现这个需求,该如何改造我们的拦截器:

方案一:

改造后的handler

public abstract class NumberHandler {
    /**
     * 处理拦截器的方法
     * @param num 待处理的数字
     * @return 处理后的数字
     */
    abstract int handleBefore(int num);

    abstract int handleAfter(int num);
}

然后看下之前的EvenNumHandler:

public class EvenNumHandler extends NumberHandler {
    @Override
    int handleBefore(int num) {
       if(num % 2 == 0){
           num -= 1;
       }
        return num;
    }

    @Override
    int handleAfter(int num) {
        return num;
    }
}

看下实现新增需求的handler:

public class NineHundredHandler extends NumberHandler {
    @Override
    int handleBefore(int num) {
        return num;
    }

    @Override
    int handleAfter(int num) {
        if(num == 990){
            num -= 1;
        }
        return num;
    }
}

然后看下调用:

public static int handle(int num){
 
        List<NumberHandler> handlers = new ArrayList<>();
        handlers.add(new EvenNumHandler());
        handlers.add(new NinetyNineHnadler());
        handlers.add(new NineHundredHandler());
        int res = num;
        for(NumberHandler handler : handlers){
            res = handler.handleBefore(res);
        }
        res = res * 10;
        for(NumberHandler handler : handlers){
            res = handler.handleAfter(res);
        }
        return res ;
    }

我们发现这种做法确实实现了请求参数(乘以10之前的数)拦截和返回值的拦截(乘以10之后的数),但这种做法有缺陷:

  • 进行了两次循环
  • 乘以10这个功能无法放置到拦截器中
  • before和after方法很容易遗漏,比如我只关注before方法,不小心就会把After方法返回0

方案二

方案一使用了两个方法来分别拦截入参和返回值,从而带来了一些缺点,那我能不能用一个方法实现呢?
仔细分析下其实是可行的:

public class EvenNumHandler extends NumberHandler {
    @Override
    int handleNum(int num) {
        if (num % 2 == 0) {
            num -= 1;
        }
        //核心在这句
        NumberHandler next = getNextHandler();
        num = next.handleNum(num);
        if(num == 990){
            num -= 1;
        }
        return num;
    }
}

可以看到实现的关键就在于先把数据交给下一个拦截器处理,下一个拦截器返回成功后,我们再处理返回值。
这样写我们要解决两个问题:

  • 在一个拦截器中如何获取另一个拦截器
  • 其他开发人员开发拦截器时如何知道要先调用下一个拦截器的方法
    这两个问题是每一个拦截器开发人员都需要考虑的,因此我们完全可以将其封装起来.首先是如何获取下一个拦截器处理的结果,我们新增了一个接口:
public interface Chain {
    /**
     * 获取下一个拦截器处理的结果
     * @param num 此次拦截器处理的入参
     * @return 下一个拦截器处理完成的结果
     */
    int proceed(int num);

    /**
     * 获取要处理的数字
     * @return 需要处理的数字
     */
    int getNum();
}

然后看下他的默认实现:

public class RealChain implements Chain {
    /**
     * 当前是第几个拦截器
     */
    private int index;
    private List<NumberHandler>numberHandlers;
    /**
     * 待处理的数字
     */
    private int num;


    public RealChain(int index, List<NumberHandler> numberHandlers, int num) {
        this.index = index;
        this.numberHandlers = numberHandlers;
        this.num = num;
    }

    @Override
    public int proceed(int num) {
        if(numberHandlers == null || numberHandlers.size() == 0){
            throw new IllegalArgumentException("未设置任何拦截器");
        }
        if(index  >= numberHandlers.size()){
            //没有更多的拦截器了,直接返回这个num
            return num;
        }
        RealChain nextChain = new RealChain(index+1,numberHandlers,num);
        //由于我们在构造
        NumberHandler handler = numberHandlers.get(index);
        return handler.handleNum(nextChain);

    }

    @Override
    public int getNum() {
        return num;
    }
}

这个Chain之所以能获取下一个拦截器的结果就在于我们在Chain里面保存了所有的拦截器和当前索引,能够通过位置找到下一个拦截器,既然RealChain里面都放了这些了,索性将要处理的number也放进去好了.
然后我们看下此时的拦截器应该怎么写:

public class EvenNumHandler extends NumberHandler {
    @Override
    int handleNum(Chain chain) {
        int num = chain.getNum();
        if (num % 2 == 0) {
            num -= 1;
        }
        //获取下一个拦截器处理的结果
        int responseNum = chain.proceed(num);
        //此处处理返回后的结果
        if(responseNum == 970){
            responseNum -= 1;
        }
        return responseNum;
    }
}

然后我们看下调用:

 public static int handle(int num){
        List<NumberHandler> handlers = new ArrayList<>();
        handlers.add(new EvenNumHandler());
        handlers.add(new NinetyNineHnadler());
        //必须保证最后一个拦截器是实现乘以10的核心拦截器
        handlers.add(new TenTimesHandler());
        RealChain chain = new RealChain(0,handlers,num);
        return chain.proceed(num);
    }

这种调用方式很明显比之前那个两次循环优雅多了,而且乘以10的核心功能也放到了拦截器中:

public class TenTimesHandler extends NumberHandler {
    @Override
    int handleNum(Chain chain) {
        int num = chain.getNum();
        //最后一个拦截器比较特殊,这里实现核心功能,肯定是最后一个,因此无需调用chain.proceed()
        return num *10;
    }
}

这样我们就实现了所有功能都是通过拦截器来操作的f妈妈再也不用担心需求变动影响以前的业务了!!!

相关文章

  • 从零开始写拦截器

    拦截器的应用是非常广泛的,okhttp中有拦截器,spring框架中也有拦截器,这个年代,你要是不知道什么是拦截器...

  • WebService 拦截器

    ** jdk写的程序不支持拦截器,需要使用cxf框架,导入cxf的jar包** 拦截器分为In拦截器和out拦截器...

  • 拦截器Interceptor

    拦截器怎么写 拦截器配置可以从struts-default.xml中找到相关资源 拦截器拦截制定方法拦截指定的方法...

  • SpringBoot使用拦截器

    一、编写一个拦截器类 二、写一个配置类使拦截器起作用

  • SpringMvc里面拦截器是怎么写的:

    SpringMvc里面拦截器是怎么写的: SpringMvc里面拦截器是怎么写的: 有两种写法,一种是实现Hand...

  • UT010029: Stream is closed问题

    HandlerInterceptor拦截器中忘了写返回值,再次进入拦截器就会报错:UT010029: Stream...

  • [Android]-[Retrofit]-[kotlin] 自定

    先看配置 OkHttpClient Retrofit 写拦截器 继承Interceptor,重写方法interce...

  • 写一个简单的Python解释器

    注:采转归档,自己学习查询使用 用 Python 从零开始写一个简单的解释器(1)用 Python 从零开始写一个...

  • Spring 实现多个拦截器

    拦截器文件 API拦截器 ADMIN拦截器

  • OkHttp之拦截器(二)

    本篇文章主要介绍OkHttp的默认拦截器 重试拦截器 桥接拦截器 缓存拦截器 连接拦截器 访问服务器拦截器 通过拦...

网友评论

    本文标题:从零开始写拦截器

    本文链接:https://www.haomeiwen.com/subject/vvlexqtx.html