问题描述
我有一个Java程序,该程序调用外部API(在下面的代码中为RealApi
),有时我想避免调用此API,而是返回预先构造的响应(由FakeApi
生成)。
因此,我最终在大多数方法中都复制了这种结构:
public Type1 m1(String s) {
try {
Type1 r = FakeApi.m1(s);
if (r != null) {
return r;
}
} catch (Exception e) {
// log error
}
return RealApi.m1(s);
}
有哪些选择可以避免在各处重复使用try / catch块?重要的是,如果FakeApi
引发异常或返回null,则必须调用RealApi
。
解决方法
一个选项是将错误检查行为封装到自己的方法中:
public <T> T fakeOrReal(Supplier<T> fake,Supplier<T> real) {
try {
T r = fake.get();
if (r != null) {
return r;
}
}
catch (Exception e) {
// log error
}
return real.get();
}
然后您可以使用来调用它
public Type1 m1(String s) {
return fakeOrReal(() -> FakeApi.m1(s),() -> RealApi.m1(s));
}
,
这并不像Thomas Preißler's answer那样简单,但是它将帮助您完全不重复任何方法。因此,如果扩展接口,则只需要修改具体的类,而无需修改描述所需实际行为的链接器。
创建一个包含RealApi
的所有方法的接口:
interface Api {
Type1 m1(String s);
}
然后是一个进行实际调用的类:
class ConcreteApi implements Api {
public Type1 m1(String s) {
return RealApi.m1(s);
}
}
然后创建您的FakeApi:
class TotallyFakeApi implements Api {
public Type1 m1(String s) {
return FakeApi.m1(s);
}
}
现在,避免重复自己的棘手部分:
private static Object callImplementation(Api api,Method method,Object[] methodArgs) throws Exception {
Method actualMethod = api.getClass().getMethod(actualMethod.getName(),actualMethod.getParameterTypes());
return actualMethod.invoke(api,methodArgs);
}
Api fakeOrReal(Api fakeApi,Api realApi) {
return (Api) Proxy.newProxyInstance(
FakeApi.class.getClassLoader(),new Class[]{Api.class},(proxy,method,methodArgs) -> {
try {
Object r = callImplementation(fakeApi,methodArgs);
if (r != null) {
return r;
}
} catch (Exception e) {
// logError(e);
}
return callImplementation(realApi,methodArgs);
}
);
}
获得这样的实际实现:
Api apiToUse = fakeOrReal(new TotallyFakeApi(),new ConcreteApi());