I'm trying to find a nice way of implementing a service which relies on a third-party library class. I also have a 'default' implementation to use as fallback in case the library is unavailable or can not provide an answer.
public interface Service {
public Object compute1();
public Object compute2();
}
public class DefaultService implements Service {
@Override
public Object compute1() {
// ...
}
@Override
public Object compute2() {
// ...
}
}
The actual implementation of the service would be something like:
public class ServiceImpl implements Service {
Service defaultService = new DefaultService();
ThirdPartyService thirdPartyService = new ThirdPartyService();
@Override
public Object compute1() {
try {
Object obj = thirdPartyService.customCompute1();
return obj != null ? obj : defaultService.compute1();
}
catch (Exception e) {
return defaultService.compute1();
}
}
@Override
public Object compute2() {
try {
Object obj = thirdPartyService.customCompute2();
return obj != null ? obj : defaultService.compute2();
}
catch (Exception e) {
return defaultService.compute2();
}
}
}
The current implementation seems to duplicate things a bit in the way that only the actual calls to the services are different, but the try/catch and the default mechanism are pretty much the same. Also, if another method was added in the service, the implementation would look almost alike.
Is there a design pattern that might apply here (proxy, strategy) to make the code look better and make further additions less copy-paste?
A proxy can probably help you here. The example below is untested, but should give you an idea of what you could put in place:
public class FallbackService implements InvocationHandler {
private final Service primaryService;
private final Service fallbackService;
private FallbackService(Service primaryService, Service fallbackService) {
this.primaryService = primaryService;
this.fallbackService = fallbackService;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Object result = method.invoke(primaryService, args);
if (result != null) return result;
} catch (Exception ignore) {}
return method.invoke(fallbackService, args);
}
public static Service createFallbackService(Service primaryService, Service fallbackService) {
return (Service) Proxy.newProxyInstance(
Service.class.getClassLoader(),
new Class[] { Service.class },
new FallbackService(primaryService, fallbackService)
);
}
}
One of the best libraries for that is Netflix' Hystrix. I am not sure if you need such a heavy lifting. It will give you thread pool, timeouts, fallbacks, monitoring, runtime configuration changes, short circuit, etc.
Basically it's a library to defend your code from failures of its dependencies.
You can extract the common logic into a separate method using method references, like:
public class ServiceImpl implements Service {
Service defaultService = new DefaultService();
ThirdPartyService thirdPartyService = new ThirdPartyService();
@Override
public Object compute1() {
return run(thirdPartyService::customCompute1, defaultService::compute1);
}
@Override
public Object compute2() {
return run(thirdPartyService::customCompute2, defaultService::compute2);
}
private static <T> T run(Supplier<T> action, Supplier<T> fallback) {
try {
T result = action.get();
return result != null ? result : fallback.get();
} catch(Exception e) {
return fallback.get();
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With