把"重试"抽象出来做个工具类吧

背景介绍

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

今天这篇文章就是把一个调用服务的重试功能抽取出一个工具类,以备复用。这里为了方便介绍,把调用服务简化成方法的调用,被调用的 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” 了

总结

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

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

原文地址:https://www.cnblogs.com/dingaimin/p/12306409.html

时间: 2024-10-12 02:44:04

把"重试"抽象出来做个工具类吧的相关文章

Unity+NGUI打造网络图片异步加载与本地缓存工具类(一)

我们在移动端的开发中,异步网络图片加载用的非常的多,在unity当中虽然有AssetBundle的存在,一般是先加载好游戏资源然后再进入场景,但是还有不少地方能够用到异步网络图片的加载以及其缓存机制. 我之前也写过两个版本的ios中的异步网络图片加载helper类,所以今天按照同样的思路,也想做一个好用的helper类给大家使用以及简单的说下实现原理. 首先我们加载一张网络图片,要做的事情分步来讲为: 0.开始之前设置一张固定的图片作为占位图(placeholder),表示我们的图片还没加载好,

Java8 增强工具类 Arrays 笔记

随手笔记做Arrays工具类分为两类单线程和多线程 以下所有带有下标范围限定的,都为含头不含尾 单线程:1.binarySearch:查找数组中为 key 的 下标 :binarySearch 二分法查找,数组必须有序,且存在此数组中,否则返回负数下标 Arrays.binarySearch(Object[] a,Object key):intArrays.binarySearch(Object[] a, int fromIndex, int toIndex,Object key):int跳至例

【SSH三大框架】Hibernate基础第二篇:编写HibernateUtil工具类优化性能

相对于上一篇中的代码编写HibernateUtil类以提高程序的执行速度 首先,仍然要写一个javabean(User.java): package cn.itcast.hibernate.domain; import java.util.Date; public class User { private int id; private String name; private Date birthday; public int getId() { return id; } public voi

连接和关闭资源工具类

做一个工具类,里面封装了两个方法,一个用来获得连接,一个用来关闭资源 package com.sjx.tool; import java.io.FileInputStream; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.S

Java并发工具类之线程间数据交换工具Exchanger

Exchanger是一个用于线程间协做的工具类,主要用于线程间的数据交换.它提供了一个同步点,在这个同步点,两个线程可以彼此交换数据.两个线程通过exchange方法交换数据,如果一个线程执行exchange方法,它就会等待另一个线程执行exchange方法,当两个线程都到达了同步点,这两个线程就可以交换数据.将本线程产生的数据传送给对方. Exchanger可用于工作的互相校对,比如我们要把线下产生的交易数据通过人工录入的方式添加到系统中,为了避免错误,我们采用AB两人同时录入的方式,当录入完

工具类小程序分享

闲时自己做的工具类小程序分享尝鲜,包含举例(铁路12306火车正晚点查询和检票口查询.记事本.小游戏.视频播放模拟器等等......) 1.小程序包含可移动菜单.swiper轮播推荐.热门推荐.实用工具类.休闲益智和我的模块. 2.欢迎小伙伴们尝鲜,欢迎小伙伴的意见和建议,可以通过本人的小程序或者该帖子联系我. 以下是小程序入口: swiper轮播推荐效果图: 工具类效果图: 休闲益智类效果图: 以下:提供两个快捷访问铁路12306查询的小程序入口: 原文地址:https://www.cnblo

为什么工具类App,都要做一个社区?

非著名程序猿涩郎 非著名程序员,字耿左直右,号涩郎.爱搞机,爱编程,是爬行在移动互联网中的一名码匠! 个人微信号:loonggg,微博:涩郎.专注于移动互联网的开发和研究.本号致力于分享IT技术和程序员工作心得体会. 欢迎大家关注与转载. 为什么工具类App.都要做一个社区? 非著名程序猿 移动互联网的蓬勃发展,以至于应用市场上App数以亿计.工具类App甚多,那天我在知乎上看到了一个问题,那就是:为什么工具类App.无论实用没用,都喜欢加上一个社区呢?当然以下的回答也是五花八门. 并且他们答的

IP工具类-自己动手做个ip解析器

导入依赖包: 下载地址:http://yunpan.cn/cLHfYZQMn65Qq  访问密码 6090 如果不用阿里巴巴的请求.则不用导入阿里巴巴的jar包.只需导入解析纯真数据库需要的jar即可. JSon包可导可不导. 以下是详细的实现代码: package com.souvc.common.base; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import

结合AnyChart做报表:一个生成AnyChart图形XML数据的工具类

今天头有点痛,所以不能详细地写了,先把代码贴上来,等身体状况稍微好一点,再继续完善. 1.(主角)一个使用XML模板生成Anychart XML数据的工具类 /** * */ package com.common.anychart; import java.io.InputStream; import java.util.List; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.l