zoukankan      html  css  js  c++  java
  • 把"重试"抽象出来做个工具类吧

    背景介绍

    我们在工作中难免会写一些重复性的代码,所以需要我们具备一定的抽象能力,比如把共同的逻辑抽取到抽象类中,也可以通过一些工具类来避免冗余代码

    今天这篇文章就是把一个调用服务的重试功能抽取出一个工具类,以备复用。这里为了方便介绍,把调用服务简化成方法的调用,被调用的 foo 方法如下:

    public static List<String> foo() {// 没有显示抛出异常
        System.out.println("调用方法");
            // 模拟抛出异常
        System.out.println(1/0);
        List<String> list = new ArrayList<>();
        list.add("1");
        return list;
    }
    

    调用方和重试逻辑如下:

    List<String> result = null;
    // 重试次数
    int retryCount = 0;
    // 调用服务的开关,默认打开
    boolean callSwitch = true;
    // 只要调用服务开关开着,并且重试次数不大于最大的重试次数,就调用服务
    while (callSwitch && retryCount <= 3) {
        try {
            // 调用服务
            result = foo();
            // 省略了对结果的校验,假设到了这里就说明没有问题,把调用服务开关关掉
            callSwitch = false;
        } catch (Exception e) {
            // 发生了异常(比如超时,就需要重试了)
            // 调用服务的开关打开
            callSwitch = true;
            retryCount++;
        }
    }
    // 后面对 result 进行处理
    

    其实上面的代码就已经解决了,服务重试的逻辑,测试没有问题后,可以提交代码让同事帮忙进行 CR 了,可是小朋同学看到这个代码后,给了建议:

    可以抽象一层,提出一个 retry 的工具类,这样大家都可以简单复用你的 retry 逻辑

    抽象思考过程

    白牙心想,也对哈,那就提出一个工具类吧,可是发了会儿呆,竟然没有头绪(反映出了抽象能力的薄弱,平时要多注意抽象能力的培养)。小朋见状,给了一点提示,白牙立马在键盘上噼里啪啦敲击了起来,就有了下面的工具类

    主要依赖函数式接口 Supplier 和 BiFunction

    public class RetryUtil {
        public static <T> T retry(int maxRetryCount, Supplier<T> supplier, BiFunction<T, Exception, Boolean> consumer) {
            T result = null;
            Exception exception = null;
    
            int retryCount = 0;
            boolean callMethod = true;
            while (callMethod && retryCount <= maxRetryCount) {
                try {
                    // 获取调用服务的结果
                    result = supplier.get();
                } catch (Exception e) {
                    // 如果重试次数不小于最大重试次数,就抛出异常,我们把对异常的处理交给业务方
                    if (retryCount >= maxRetryCount) {
                        throw e;
                    }
                    exception = e;  
                }
                // 对结果进行判断
                callMethod = consumer.apply(result, exception);
                if (callMethod) {
                    retryCount++;
                }
            }
            return result;
        }
    }
    

    业务调用方的代码如下:

    List<String> result1 = retry(3,// 最大重试次数
                    ()-> foo(),// 调用服务
                    (list, e) -> e != null || list == null || list.isEmpty());// 对结果处理
    

    自测没有问题后,又提交代码让小朋给 CR 一下,小朋凝视了会儿,就有了下面的对话

    小朋:“retry 方法没有抛出异常”

    白牙:“被调用的服务没有显示的抛出异常,这里也就没有抛出”

    小朋:“那人如果有服务方显示抛出异常呢?”

    白牙:“我再改一版”

    服务方显示抛出了异常,这样 retry 方法也得显示抛出异常,但调用方就会显示会处理的异常,如下所示:

    public static List<String> foo() throws Exception{// 显示抛出异常
        System.out.println("调用方法");
            // 模拟抛出异常
        System.out.println(1/0);
        List<String> list = new ArrayList<>();
        list.add("1");
        return list;
    }
    
    public class RetryUtil {
        public static <T> T retry(int maxRetryCount, Supplier<T> supplier, BiFunction<T, Exception, Boolean> consumer) throws Exception{
            // 省略...
    }
    

    提示未处理的异常

    出现这种情况是因为 Supplier 的 get 方法没有抛出异常

    @FunctionalInterface
    public interface Supplier<T> {
        /**
         * Gets a result.
         *
         * @return a result
         */
        T get();
    }
    

    既然你不支持,那就自己写个呗,于是就有了下面的 DspSupplier,它和 Supplier 的主要区别就是在 get 方法中显示抛出了异常

    @FunctionalInterface
    interface DspSupplier<T> {
        /**
         * Gets a result.
         *
         * @return a result
         */
        T get() throws Exception;
    }
    

    于是 retry 方法就变成了下面这样子

    public class RetryUtil {
        public static <T> T retry(int maxRetryCount, DspSupplier<T> supplier, BiFunction<T, Exception, Boolean> consumer) throws Exception{
            // 省略...
    }
    

    使用了自定义的 Supplier 后,调用方就没有 “Unhandled exception” 了

    总结

    我们平时再开发的过程中,可以尝试去利用函数式接口去实现一些逻辑的抽取,做成一个工具类,供大家使用,简化人力,也是对自己编码能力的一个提升。

    上面的案例比较简单,但麻雀虽小,五脏俱全,也是一个不错的体验
    欢迎关注公众号 【每天晒白牙】,获取最新文章,我们一起交流,共同进步!

  • 相关阅读:
    innodb临键锁锁定范围
    详解 MySql InnoDB 中的三种行锁(记录锁、间隙锁与临键锁)
    解决Jenkins邮件配置问题
    解决import模块后提示无此模块的问题
    【转】Linux下cp: omitting directory `XXX'问题解决
    Python之异常处理(执行python文件时传入参数)
    Python之发邮件
    Python之递归
    Python之参数类型、变量
    linux sed命令详解
  • 原文地址:https://www.cnblogs.com/dingaimin/p/12306409.html
Copyright © 2011-2022 走看看