10

Я пытаюсь найти хороший способ реализации службы, которая опирается на сторонний класс библиотеки. У меня также есть реализация по умолчанию, используемая в качестве резервной копии, если библиотека недоступна или не может предоставить ответ.Java fallback pattern

public interface Service { 

    public Object compute1(); 

    public Object compute2(); 
} 

public class DefaultService implements Service { 

    @Override 
    public Object compute1() { 
     // ... 
    } 

    @Override 
    public Object compute2() { 
     // ... 
    } 
} 

Фактическая реализация сервиса будет что-то вроде:

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(); 
     } 
    } 
} 

Текущая реализация кажется дублировать вещи немного в пути, что только реальные вызовы на услуги разные, но try/catch, а механизм по умолчанию - почти то же самое. Кроме того, если в сервисе был добавлен другой метод, реализация будет выглядеть почти одинаково.

Есть ли шаблон дизайна, который может применяться здесь (proxy, strategy), чтобы код выглядел лучше и делал дальнейшее добавление меньше копий-пасты?

+0

Что версию Java вы используете? – Radiodef

ответ

3

Вы можете извлечь общую логику в отдельный метод с использованием ссылок методов, как:

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(); 
     } 
    } 
} 
+1

Я сомневаюсь, что это решение может быть применено, если методы принимают аргументы. Вам придется перегружать 'run' для, по крайней мере,' Function' и 'BiFunction', а затем вам может понадобиться [' TriFunction'] (http://stackoverflow.com/questions/18400210/java-8-where-is -trifunction-and-kin-in-java-util-function-or-what-is-the-alt), но он не очень хорошо масштабируется. Более того, каждому новому методу «Сервис» нужна соответствующая реализация в «ServiceImpl», которая также не масштабируется, а также может быть источником ошибок и проблем обслуживания. –

+0

Согласен с @Didier, хотя это решение для этого конкретного случая, это не очень хорошее общее решение. Обработчики методов являются классными, но, вероятно, не так хорошо подходят здесь. – Guillaume

2

Одна из лучших библиотек для этого - Netflix 'Hystrix. Я не уверен, нужен ли вам такой тяжелый подъем. Это даст вам пул потоков, тайм-ауты, резервные копии, мониторинг, изменения конфигурации времени выполнения, короткое замыкание и т. Д.

В основном это библиотека, защищающая ваш код от сбоев его зависимостей.

+0

Hystrix отлично смотрится! Я оцениваю это прямо сейчас. Он обеспечивает гораздо больше, чем то, о чем просил OP, но это может быть хорошо ... – Guillaume

+0

Спасибо, что упомянул Hystrix. Не знал об этом, но он делает больше, чем то, что мне нужно, и у нас есть довольно строгая политика в отношении добавления библиотек. Вы проверите это, хотя – user4132657

3

Возможно, вам может помочь прокси-сервер. Приведенный ниже пример не тестировался, но должен дать вам представление о том, что вы могли бы поставить на место:

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) 
     ); 
    } 
}