Передать функцию и аргументы другой функции - PullRequest
0 голосов
/ 23 мая 2019

У меня есть некоторые функции, которые выполняют те же операции в блоке catch.

private void fun1(int a){
  try{
    // do api calls
  }catch(Exception e){
    refreshToken();
    fun1(a);
  }
}

private int fun2(int a, String b){
  try{
    // do api calls
  }catch(Exception e){
    refreshToken();
    fun2(a,b);
  }
}

private void fun3(String a, long b, char c){
  try{
    // do api calls
  }catch(Exception e){
    refreshToken();
    fun3(a,b,c);
  }
}

Здесь, когда истекает токен приложения, мне нужно вызвать функцию для извлечения нового токена и вызвать родительскую функцию.

В блоке catch код дублируется. Поэтому я хотел бы выполнить эти операции в одной функции. Для этого мне нужно передать функцию и аргументы. Возможно ли это сделать в Java?

Ответы [ 5 ]

2 голосов
/ 23 мая 2019

Если вам требуется такая функциональность чаще, вы можете захотеть создать для этого собственную утилиту поддержки (которую вы также можете улучшить в зависимости от своих потребностей), например ::

public class Retry {
  public static <T> T endlessTryCall(Callable<T> callable, Consumer<Exception> exceptionHandler) {
    for(;;) try {
        return callable.call();
      } catch (Exception e) {
        exceptionHandler.accept(e);
      }
  }

  public static void endlessTryRun(Runnable runnable, Consumer<Exception> exceptionHandler) {
    for(;;) try {
        runnable.run();
        return;
      } catch (Exception e) {
        exceptionHandler.accept(e);
      }
  }
}

Ваши функции могут выглядеть следующим образом:

private void fun1(int a){
  Retry.endlessTryRun(() -> {
    // the API calls
  }, e -> refreshToken());
}

private int fun2(int a, String b){
  return Retry.endlessTryCall(() -> {
    // the API calls
    return ...;
  }, e -> refreshToken());
}

private void fun3(String a, long b, char c){
  Retry.endlessTryRun(() -> {
    // the API calls
    }, e -> refreshToken());
}

Дополнительные улучшения могут заключаться в том, чтобы иметь что-то вроде tryCall(numberOfTrials, ...) -функции или другую функцию, которая обрабатывает исключение так, как вам нужно. Демонстрируя последнее:

private void fun1(int a){
  refreshTokenOnException(() -> {
    // the API calls
  });
}

private int fun2(int a, String b){
  return refreshTokenOnException(() -> {
    // the API calls
    return ...;
  });
}

private void fun3(String a, long b, char c){
  refreshTokenOnException(() -> {
    // the API calls
  });
}

private <T> T refreshTokenOnException(Callable<T> callable) {
  return Retry.endlessTryCall(callable, e -> refreshToken());
}
private void refreshTokenOnException(Runnable runnable) {
  Retry.endlessTryRun(runnable, e -> refreshToken());
}

Преимущество наличия такой утилиты на месте, вероятно, сомнительно. Я думаю, что это немного более многословно относительно того, что сделано. Но опять же, нужно найти подходящие имена для него ... так что ... как всегда.

1 голос
/ 23 мая 2019

Вы уже сделали самую разумную вещь здесь, извлекли общий код в метод refreshToken() и вызывайте этот метод в каждом месте, где это необходимо. Вот как мы это делаем, и «дублирование кода» одного вызова метода является приемлемым. Не конструктивно взрывать код во что-то гораздо более сложное, даже если этот сложный код состоит из множества разных шаблонов для каждого случая.

Единственное, от чего вам следует держаться подальше, это повторить операцию через рекурсию. Поскольку аннулирование текущего токена может происходить произвольное количество раз, так как вы не контролируете, сколько времени займет оценка, может произойти произвольное количество рекурсий, что повышает риск StackOverflowError. Кроме того, рекурсивный вызов действительно является кодом, которого можно избежать.
Просто используйте петли.

private void fun1(final int a) {
  for(;;) try {
    // do api calls, don't modify a
    return;
  } catch(Exception e) {
    refreshToken();
  }
}

private int fun2(final int a, final String b) {
  try {
    // do api calls, don't modify a nor b
    return result;
  } catch(Exception e) {
    refreshToken();
  }
}

private void fun3(final String a, final long b, final char c) {
  for(;;) try {
    // do api calls, don't modify a, b, nor c
    return;
  } catch(Exception e) {
    refreshToken();
  }
}
0 голосов
/ 24 мая 2019

рассмотрите возможность использования библиотеки, подобной Failsafe , которая предлагает гораздо большую гибкость в обработке различных случаев ошибок.

Прежде всего вам необходимо объявить политику повторных попыток, которая определяет, какие ошибки должны бытьобрабатываются и когда повторные попытки должны быть выполнены.Вы хотите повторить попытку только по истечении срока действия токена, а не при появлении другой ошибки.

RetryPolicy<Object> retryPolicy = new RetryPolicy<>()
            .handle(TokenExpiredException.class)
            .withMaxRetries(3);

и вызвать ее следующим образом

Failsafe.with(retryPolicy)
            .onFailure(e -> refreshToken())
            .run(() -> fun1(42));

Кроме того, она может использоваться с (проверено) поставщик, как в вашем случае с fun2 (возвращает int):

int result = Failsafe.with(retryPolicy)
                .onFailure(e -> refreshToken())
                .get(() -> fun2(12, "cat"));
0 голосов
/ 23 мая 2019
package be.test;

public class FuncTest {

    private void fun1(int a) {
        // do api calls
    }

    private int fun2(int a, String b) {
        // do api calls
        return 0;
    }

    private void fun3(String a, long b, char c) {
        // do api calls
    }

    private void refreshToken() {
        // do your refresh token logic
    }

    // functional interface
    public interface MyFunc {
        public void doIt();
    }

    public void callWithRefresh(MyFunc func)
    {
        try {
            func.doIt();
        } catch (Exception e) {
            refreshToken();
            func.doIt();
        }
    }
    public static void main(String[] args) {
        FuncTest ftest=new FuncTest();
        ftest.callWithRefresh(()->ftest.fun1(1));
        ftest.callWithRefresh(()->ftest.fun2(1,"hopla"));
        ftest.callWithRefresh(()->ftest.fun3("hopla",1,'c'));
    }
}

callWithRefresh содержит общую логику для вызова refreshToken при возникновении исключения.(надеясь, что причина исключения связана с просроченным токеном:)

MyFunc - это просто интерфейс для вызова doIt ().Нет необходимости реализовывать этот интерфейс в любом месте.

0 голосов
/ 23 мая 2019

Вы можете сделать обходной путь и сделать «switch ... case», и дать ключ переключения в качестве параметра ...

public void catchCase(...){
refreshToken();
switch(expression) {
  case x:
    fun1(...)
     break;
  case y:
    fun2(...)
    break;
  case z:
    fun3(...)
  default:
    // code block

} }

вам нужно будет указать все параметры для всех трех функций в качестве аргумента ...

Другой вариант - использование Java-отражения

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...