10

Próbuję znaleźć dobry sposób na wdrożenie usługi, która opiera się na klasie biblioteki innej firmy. Mam również "domyślną" implementację, która może być użyta jako element zapasowy w przypadku, gdy biblioteka jest niedostępna lub nie może udzielić odpowiedzi.Wzór awaryjny Java

public interface Service { 

    public Object compute1(); 

    public Object compute2(); 
} 

public class DefaultService implements Service { 

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

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

Faktyczna realizacja usługi będzie coś takiego:

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

Obecna implementacja zdaje się powielać rzeczy trochę w ten sposób, że jedynie rzeczywiste połączenia z usługami są różne, ale try/catch i domyślny mechanizm są prawie takie same. Ponadto, jeśli dodano inną metodę w usłudze, implementacja wyglądałaby prawie identycznie.

Czy istnieje wzór, który może mieć tutaj zastosowanie (proxy, strategy), aby kod wyglądał lepiej i tworzyć dalsze dodatki, pomniejszając kopiowanie i wklejanie?

+0

Co wersja Java jest używana? – Radiodef

Odpowiedz

3

można wyodrębnić wspólne logiki do osobnej metody pomocą odwołań metody, jak:

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

Wątpię, czy to rozwiązanie może być ładnie zastosowane, jeśli metody przyjmują argumenty. Musiałbyś przeciążyć 'run' dla przynajmniej' Function' i 'BiFunction', a wtedy możesz potrzebować [' TriFunction'] (http://stackoverflow.com/questions/18400210/java-8-where-is -trifunction-and-kin-in-java-util-function-or-what-is-the-the-alt), ale nie skaluje się zbyt dobrze. Co więcej, każda nowa metoda 'Service' wymaga odpowiedniej implementacji w' ServiceImpl', która również nie jest skalowalna i może być źródłem błędów i problemów konserwacyjnych. –

+0

Uzgodniono z @Didier, a jest to rozwiązanie dla tego konkretnego przypadku, nie jest to bardzo dobre ogólne rozwiązanie. Uchwyty metod są fajne, ale prawdopodobnie nie pasują tutaj. – Guillaume

2

Jedną z najlepszych bibliotek jest Netflix 'Hystrix. Nie jestem pewien, czy potrzebujesz takiego ciężkiego podnoszenia. Da ci pulę wątków, limity czasu, awarie, monitorowanie, zmiany konfiguracji środowiska wykonawczego, zwarcia, itp.

Zasadniczo jest to biblioteka do obrony twojego kodu przed awariami jego zależności.

+0

Hystrix wygląda świetnie! Oceniam to teraz. Zapewnia o wiele więcej niż to, o co prosił OP, ale to może być dobre ... – Guillaume

+0

Dziękuję za wzmiankę o Hystrix. Nie zdawałem sobie z tego sprawy, ale robi to znacznie więcej niż to, czego potrzebuję i mamy dość surową politykę dotyczącą dodawania bibliotek. Czy to sprawdzę? – user4132657

3

Prokurent może ci w tym pomóc. Poniższy przykład jest niesprawdzone, ale powinien dać wyobrażenie o tym, co można umieścić w miejscu:

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