好的,标题可能很难理解。我找不到正确的东西。
所以,基本上我使用Java 8函数来创建一个可重试的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界面中创建一种“构造函数”(这不是正确的单词,因为接口不能有构造函数),但我不知道如何。有人有想法吗?
答案 0 :(得分: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); }
}
}
}
只需要一个实现类,它必须能够处理参数和返回值,其他人可以使用适配器函数委托给它,执行任一操作,删除参数或返回{{1}或两者兼而有之。
对于大多数用例,lambda表达式的形状适合选择正确的方法,例如
null
但有时需要一点提示:
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);
答案 1 :(得分: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而不是例如有些人打电话给retrying
或skipping
稍后抛出NPE。