2017-08-07 47 views
3

好的,标题可能很难理解。我没有找到正确的东西。 所以,基本上我使用Java 8函数来创建一个Retryable API。我想要简单地实现这些接口,所以我在Retryable接口的每个实现中创建了一个of(...)方法,我们可以使用lambda表达式,而不是手动创建匿名类。为内部子接口创建一个“默认构造函数”

import java.util.function.Consumer; 
import java.util.function.Function; 
import java.util.function.Supplier; 

public interface Retryable<T, R> extends Function<T, R>{ 

    void retrying(Exception e); 

    void skipping(Exception e); 

    int trials(); 

    @Override 
    default R apply(T t) { 
     int trial = 0; 
     while (true) { 
      trial++; 
      try { 
       return action(t); 
      } catch (Exception e) { 
       if (trial < trials()) { 
        retrying(e); 
       } else { 
        skipping(e); 
        return null; 
       } 
      } 
     } 
    } 

    R action(T input) throws Exception; 

    interface RunnableRetryable extends Retryable<Void, Void> { 

     static RunnableRetryable of(Consumer<Exception> retrying, Consumer<Exception> skipping, int trials, CheckedRunnable runnable) { 
      return new RunnableRetryable() { 
       @Override 
       public void retrying(Exception e) { 
        retrying.accept(e); 
       } 

       @Override 
       public void skipping(Exception e) { 
        skipping.accept(e); 
       } 

       @Override 
       public int trials() { 
        return trials; 
       } 

       @Override 
       public Void action(Void v) throws Exception { 
        runnable.tryRun(); 
        return null; 
       } 
      }; 
     } 

     @FunctionalInterface 
     interface CheckedRunnable extends Runnable { 

      void tryRun() throws Exception; 

      @Override 
      default void run() { 
       try { 
        tryRun(); 
       } catch (Exception e) { 
        throw new RuntimeException(e); 
       } 
      } 
     } 
    } 

    interface ConsumerRetryable<T> extends Retryable<T, Void> { 

     static <T> ConsumerRetryable of(Consumer<Exception> retrying, Consumer<Exception> skipping, int trials, CheckedConsumer<T> consumer) { 
      return new ConsumerRetryable<T>() { 
       @Override 
       public void retrying(Exception e) { 
        retrying.accept(e); 
       } 

       @Override 
       public void skipping(Exception e) { 
        skipping.accept(e); 
       } 

       @Override 
       public int trials() { 
        return trials; 
       } 

       @Override 
       public Void action(T t) throws Exception { 
        consumer.tryAccept(t); 
        return null; 
       } 
      }; 
     } 

     @FunctionalInterface 
     interface CheckedConsumer<T> extends Consumer<T> { 

      void tryAccept(T t) throws Exception; 

      @Override 
      default void accept(T t) { 
       try { 
        tryAccept(t); 
       } catch (Exception e) { 
        throw new RuntimeException(e); 
       } 
      } 
     } 
    } 

    interface SupplierRetryable<T> extends Retryable<Void, T> { 

     static <T> SupplierRetryable of(Consumer<Exception> retrying, Consumer<Exception> skipping, int trials, CheckedSupplier<T> supplier) { 
      return new SupplierRetryable<T>() { 
       @Override 
       public void retrying(Exception e) { 
        retrying.accept(e); 
       } 

       @Override 
       public void skipping(Exception e) { 
        skipping.accept(e); 
       } 

       @Override 
       public int trials() { 
        return trials; 
       } 

       @Override 
       public T action(Void v) throws Exception { 
        return supplier.tryGet(); 
       } 
      }; 
     } 

     @FunctionalInterface 
     interface CheckedSupplier<T> extends Supplier<T> { 

      T tryGet() throws Exception; 

      @Override 
      default T get() { 
       try { 
        return tryGet(); 
       } catch (Exception e) { 
        throw new RuntimeException(e); 
       } 
      } 
     } 
    } 

    interface FunctionRetryable<T, R> extends Retryable<T, R> { 

     static <T, R> FunctionRetryable of(Consumer<Exception> retrying, Consumer<Exception> skipping, int trials, CheckedFunction<T, R> function) { 
      return new FunctionRetryable<T, R>() { 
       @Override 
       public void retrying(Exception e) { 
        retrying.accept(e); 
       } 

       @Override 
       public void skipping(Exception e) { 
        skipping.accept(e); 
       } 

       @Override 
       public int trials() { 
        return trials; 
       } 

       @Override 
       public R action(T t) throws Exception { 
        return function.tryApply(t); 
       } 
      }; 
     } 

     @FunctionalInterface 
     interface CheckedFunction<T, R> extends Function<T, R> { 

      R tryApply(T t) throws Exception; 

      @Override 
      default R apply(T t) { 
       try { 
        return tryApply(t); 
       } catch (Exception e) { 
        throw new RuntimeException(e); 
       } 
      } 
     } 
    } 
} 

但正如你所看到的,有一个在每个of(...)方法有很多重复的代码。我可以在Retryable接口中创建一种“构造函数”(这不是正确的词,因为接口不能有构造函数),但我不知道如何。有人有想法吗?

+2

我没有看到这些冗余内部接口存在的理由。对于不同参数化的'Retryable',您只有多个工厂方法。 – Holger

回答

5

主要问题是您的API爆炸。所有延伸到Retryable的嵌套接口都不添加任何功能,但只要它们是API的一部分,就需要此代码的用户处理它们。此外,它们是导致代码重复的原因,因为这些冗余接口中的每一个都需要自己的实现,而所有的实现基本上都是一样的。

删除这些过时的类型后,你可以简单地实现操作的代表团:

public interface Retryable<T, R> extends Function<T, R>{ 
    void retrying(Exception e); 
    void skipping(Exception e); 
    int trials(); 
    @Override default R apply(T t) { 
     try { return action(t); } 
     catch(Exception e) { 
      for(int trial = 1; trial < trials(); trial++) { 
       retrying(e); 
       try { return action(t); } catch (Exception next) { e=next; } 
      } 
      skipping(e); 
      return null; 
     } 
    } 

    R action(T input) throws Exception; 

    public static Retryable<Void, Void> of(Consumer<Exception> retrying, 
      Consumer<Exception> skipping, int trials, CheckedRunnable runnable) { 
     return of(retrying, skipping, trials, x -> { runnable.tryRun(); return null; }); 
    } 

    @FunctionalInterface interface CheckedRunnable extends Runnable { 
     void tryRun() throws Exception; 
     @Override default void run() { 
      try { tryRun(); } catch (Exception e) { throw new RuntimeException(e); } 
     } 
    } 

    public static <T> Retryable<T, Void> of(Consumer<Exception> retrying, 
      Consumer<Exception> skipping, int trials, CheckedConsumer<T> consumer) { 
     return of(retrying, skipping, trials, 
        value -> { consumer.tryAccept(value); return null; }); 
    } 

    @FunctionalInterface interface CheckedConsumer<T> extends Consumer<T> { 
     void tryAccept(T t) throws Exception; 
     @Override default void accept(T t) { 
      try { tryAccept(t); } catch (Exception e) { throw new RuntimeException(e); } 
     } 
    } 

    public static <T> Retryable<Void, T> of(Consumer<Exception> retrying, 
      Consumer<Exception> skipping, int trials, CheckedSupplier<T> supplier) { 
     return of(retrying, skipping, trials, voidArg -> { return supplier.tryGet(); }); 
    } 

    @FunctionalInterface interface CheckedSupplier<T> extends Supplier<T> { 
     T tryGet() throws Exception; 
     @Override default T get() { 
      try { return tryGet(); } 
      catch (Exception e) { throw new RuntimeException(e); } 
     } 
    } 

    public static <T, R> Retryable<T, R> of(Consumer<Exception> retrying, 
      Consumer<Exception> skipping, int trials, CheckedFunction<T, R> function) { 
     return new Retryable<T, R>() { 
      @Override public void retrying(Exception e) { retrying.accept(e); } 
      @Override public void skipping(Exception e) { skipping.accept(e); } 
      @Override public int trials() { return trials; } 
      @Override public R action(T t) throws Exception { 
       return function.tryApply(t); 
      } 
     }; 
    } 

    @FunctionalInterface interface CheckedFunction<T, R> extends Function<T, R> { 
     R tryApply(T t) throws Exception; 
     @Override default R apply(T t) { 
      try { return tryApply(t); } 
      catch (Exception e) { throw new RuntimeException(e); } 
     } 
    } 
} 

只有需要一个实现类,它必须能够处理的参数和返回值时,其他人可以使用适配器功能简单地委托给它,也可以放弃参数或返回null或两者。

对于大多数使用情况,lambda表达式的形状适合于选择正确的方法,例如,

Retryable<Void,Void> r = Retryable.of(e -> {}, e -> {}, 3,() -> {}); 
Retryable<Void,String> s = Retryable.of(e -> {}, e -> {}, 3,() -> "foo"); 
Retryable<Integer,Integer> f = Retryable.of(e -> {}, e -> {}, 3, i -> i/0); 

但有时,需要一个小提示:

// braces required to disambiguate between Function and Consumer 
Retryable<String,Void> c = Retryable.of(e->{}, e ->{}, 3, 
             str -> { System.out.println(str); }); 
+0

你是完全正确的。我看起来太过分了。现在很明显。谢谢 ! – Franckyi

+0

@Holger我们也有一个重试机制(谁没有?),但OP的想法做到这一点,你的答案可能会给我们的代码一个新的形状... – Eugene

0

看起来那么您可以一些这方面出的(可能是包私有)抽象类:

abstract class AbstractRetryable<T, R> implements Retryable<T, R> { 
    private final Consumer<Exception> retrying; 
    private final Consumer<Exception> skipping; 
    private final int     trials; 
    AbstractRetryable(Consumer<Exception> retrying, 
         Consumer<Exception> skipping, 
         int     trials) { 
     this.retrying = Objects.requireNonNull(retrying, "retrying"); 
     this.skipping = Objects.requireNonNull(skipping, "skipping"); 
     this.trials = trials; 
    } 
    @Override 
    public void retrying(Exception x) { 
     retrying.accept(x); 
    } 
    @Override 
    public void skipping(Exception x) { 
     skipping.accept(x); 
    } 
    @Override 
    public int trials() { 
     return trials; 
    } 
} 

与此唯一的问题是,你正在使用的子接口,这样你就可以”创建一个匿名类,它既扩展了抽象类,又实现了子接口。

然后,您可以写更多的(再次,可能是包私有)的子类:

final class RunnableRetryableImpl 
extends AbstractRetryable<Void, Void> 
implements RunnableRetryable { 
    private final CheckedRunnable runnable; 
    RunnableRetryableImpl(Consumer<Exception> retrying, 
          Consumer<Exception> skipping, 
          int     trials, 
          CheckedRunnable  runnable) { 
     super(retrying, skipping, trials); 
     this.runnable = Objects.requireNonNull(runnable, "runnable"); 
    } 
    @Override 
    public Void apply(Void ignored) { 
     try { 
      runnable.tryRun(); 
     } catch (Exception x) { 
      // BTW I would consider doing this. 
      if (x instanceof RuntimeException) 
       throw (RuntimeException) x; 
      // I would also probably write a class like: 
      // class RethrownException extends RuntimeException { 
      //  RethrownException(Exception cause) { 
      //   super(cause); 
      //  } 
      // } 
      // This way the caller can catch a specific type if 
      // they want to. 
      // (See e.g. java.io.UncheckedIOException) 
      throw new RuntimeException(x); 
     } 
     return null; 
    } 
} 

或者你可以通过使用本地类降低线数:

static RunnableRetryable of(Consumer<Exception> retrying, 
          Consumer<Exception> skipping, 
          int     trials, 
          CheckedRunnable  runnable) { 
    Objects.requireNonNull(runnable, "runnable"); 
    final class RunnableRetryableImpl 
    extends AbstractRetryable<Void, Void> 
    implements RunnableRetryable { 
     RunnableRetryable() { 
      // Avoid explicitly declaring parameters 
      // and passing arguments. 
      super(retrying, skipping, trials); 
     } 
     @Override 
     public Void apply(Void ignored) { 
      try { 
       runnable.tryRun(); 
      } catch (Exception x) { 
       if (x instanceof RuntimeException) 
        throw (RuntimeException) x; 
       throw new RuntimeException(x); 
      } 
      return null; 
     } 
    } 
    return new RunnableRetryableImpl(); 
} 

个人而言,我想我只会编写包私有实现而不是本地类,但它肯定需要大量的样板代码。另外,作为一个方面说明,当你正在编写返回匿名类的工厂时,你应该在方法本身内部使用requireNonNull(正如我在我的例子of方法中所做的那样)。这是因为如果null被传递给该方法,该方法抛出NPE而不是例如。一些呼叫retryingskipping一段时间后抛出NPE。

+3

而不是在'catch'子句中做'instanceof',你可以简单地创建第二个具有该类型的'catch'子句, catch(RuntimeException | Error unchecked){throw unchecked; } catch(Throwable checked){抛出新的RuntimeException(checked); }' – Holger

相关问题