13

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?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user4132657
  • 207
  • 1
  • 3
  • 10

3 Answers3

4

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)
        );
    }
}
Guillaume
  • 18,494
  • 8
  • 53
  • 74
3

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
piotrek
  • 13,982
  • 13
  • 79
  • 165
  • Hystrix looks great! I'm evaluating it right now. It provides much much more than what the OP was asking for, but that can be a good thing... – Guillaume Apr 26 '15 at 11:26
  • Thank you for mentioning Hystrix. Was unaware of it, but it does way more than what I need and we have a rather strict policy about adding libs. Will check it out though – user4132657 May 02 '15 at 15:00
3

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();
        }
    }
}
fgb
  • 18,439
  • 2
  • 38
  • 52
  • 1
    I doubt this solution can nicely be applied if the methods take arguments. You would have to overload `run` for at least `Function` and `BiFunction`, and then you might need a [`TriFunction`](http://stackoverflow.com/questions/18400210/java-8-where-is-trifunction-and-kin-in-java-util-function-or-what-is-the-alt) but it doesn't scale very well. Moreover, each new method of `Service` needs a corresponding implementation in `ServiceImpl`, which does not scale well either and could also be a source of bugs and maintenance problems. – Didier L Apr 27 '15 at 11:36
  • Agreed with @Didier, while this is a solution for this specific case, it is not a very good general solution. Method handles are cool, but probably not that well suited here. – Guillaume Apr 27 '15 at 19:48
  • If `fallback.get()` throws an exception in the `try` block, it gets repeated in the `catch` block. – jaco0646 Oct 19 '18 at 20:10