Abstract Retry as a tool class

Background introduction

We inevitably write repetitive code in our work, so we need to be abstract, such as extracting common logic into abstract classes, or avoiding redundant code through some tool classes.

This article today extracts a tool class from a retry function that invokes a service for reuse.For the sake of introduction, the calling service is simplified as a method call, and the foo method invoked is as follows:

public static List<String> foo() {// No throw exception shown
    System.out.println("Call Method");
        // Analog throws an exception
    System.out.println(1/0);
    List<String> list = new ArrayList<>();
    list.add("1");
    return list;
}

The caller and retry logic is as follows:

List<String> result = null;
// retry count
int retryCount = 0;
// Switch calling service, turned on by default
boolean callSwitch = true;
// The service is invoked as long as the invocation service switch is on and the number of retries is not greater than the maximum number of retries
while (callSwitch && retryCount <= 3) {
    try {
        // Call Service
        result = foo();
        // Omitting the validation of the results, assuming that there is no problem here, turn off the call service switch
        callSwitch = false;
    } catch (Exception e) {
        // An exception has occurred (such as a timeout, which requires retrying)
        // Service call switch on
        callSwitch = true;
        retryCount++;
    }
}
// Processing result later

In fact, the above code has been solved, the logic of service retry, after the test has no problems, you can submit the code for colleagues to help CR, but after the little friend saw the code, gave suggestions:

You can abstract a layer and suggest a retry tool class so that everyone can easily reuse your retry logic

Abstract thinking process

White teeth think, yeah, then put forward a tool class, but after a while, I didn't have any idea (reflecting the weakness of abstraction ability, we should pay more attention to the cultivation of abstraction ability in peacetime).My little friend saw me and gave me a hint. White teeth immediately cracked on the keyboard and there were the following tools

Mainly dependent on functional interfaces Supplier and 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 {
                // Get the result of calling the service
                result = supplier.get();
            } catch (Exception e) {
                // If the number of retries is not less than the maximum number of retries, an exception is thrown and we hand it over to the business party
                if (retryCount >= maxRetryCount) {
                    throw e;
                }
                exception = e;  
            }
            // Judge the results
            callMethod = consumer.apply(result, exception);
            if (callMethod) {
                retryCount++;
            }
        }
        return result;
    }
}

The code for the business caller is as follows:

List<String> result1 = retry(3,// max retries
                ()-> foo(),// Call Service
                (list, e) -> e != null || list == null || list.isEmpty());// Processing results

After the self-test is OK, submit the code to let the little friend give CR a look. After the little friend stares for a while, he has the following conversation

Little friend: "The retry method did not throw an exception"

White teeth:'The invoked service does not show a throw exception, and there is no throw here'

Xiao Peng: "What if the person shows an exception thrown by the server?"

White teeth: "I'll change it again"

The service side shows that an exception was thrown, so the retry method also shows that an exception was thrown, but the caller shows the exception that will be handled, as follows:

public static List<String> foo() throws Exception{// Show Thrown Exceptions
    System.out.println("Call Method");
        // Analog throws an exception
    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{
        // Omit...
}

This happens because Supplier's get method did not throw an exception

@FunctionalInterface
public interface Supplier<T> {
    /**
     * Gets a result.
     *
     * [@return](https://my.oschina.net/u/556800) a result
     */
    T get();
}

Since you don't support it, write your own chop, and you have the following DspSupplier, the main difference between it and Supplier is that it shows that an exception was thrown in the get method

@FunctionalInterface
interface DspSupplier<T> {
    /**
     * Gets a result.
     *
     * [@return](https://my.oschina.net/u/556800) a result
     */
    T get() throws Exception;
}

So the retry method becomes

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

When a custom Supplier is used, there is no "Unhandled exception" for the caller

summary

In the process of our ordinary redevelopment, we can try to use the functional interface to achieve some logical extraction and make it into a tool class for everyone to use, simplify human resources, and also improve their coding ability.

The above case is relatively simple, but sparrows are small and full of viscera, which is also a good experience Welcome to the Public Number, Get the latest articles, and we can communicate and make progress together!

Keywords: Programming less

Added by nafarius1357 on Thu, 19 Dec 2019 06:00:39 +0200