检查默认响应时避免代码重复

问题描述

我有一个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());