10

Ich versuche eine gute Möglichkeit zu finden, einen Dienst zu implementieren, der auf einer Bibliotheksklasse von Drittanbietern beruht. Ich habe auch eine "Standard" -Implementierung, um als Fallback zu verwenden, falls die Bibliothek nicht verfügbar ist oder keine Antwort geben kann.Java-Fallback-Muster

public interface Service { 

    public Object compute1(); 

    public Object compute2(); 
} 

public class DefaultService implements Service { 

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

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

Die tatsächliche Implementierung des Dienstes wäre so etwas wie:

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

Die aktuelle Implementierung der Dinge ein wenig in der Art und Weise zu kopieren scheint, dass nur die tatsächlichen Anrufe auf die Dienste unterschiedlich sind, aber die try/catch und der Standardmechanismus sind ziemlich gleich. Wenn eine andere Methode im Service hinzugefügt wurde, würde die Implementierung ähnlich aussehen.

Gibt es ein Entwurfsmuster, das hier angewendet werden könnte (proxy, strategy), um den Code besser aussehen zu lassen und weitere Zusätze weniger Copy-Paste zu machen?

+0

Was Java-Version verwenden Sie? – Radiodef

Antwort

3

Sie die gemeinsame Logik in einem separaten Verfahren mit der Methode Referenzen extrahieren kann, wie:

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

Ich bezweifle, dass diese Lösung gut angewendet werden kann, wenn die Methoden Argumente nehmen. Sie müssten 'run' für mindestens' Function' und 'BiFunction' überladen, und dann benötigen Sie möglicherweise eine [' TriFunction'] (http://stackoverflow.com/questions/18400210/java-8-where-is) -trifunction-and-kin-in-java-util-funktion-oder-was-ist-die-alt) aber es skaliert nicht sehr gut. Darüber hinaus benötigt jede neue Methode von "Service" eine entsprechende Implementierung in "ServiceImpl", die ebenfalls nicht gut skaliert und auch eine Quelle von Fehlern und Wartungsproblemen sein kann. –

+0

Vereinbart mit @Didier, während dies eine Lösung für diesen speziellen Fall ist, ist es keine sehr gute allgemeine Lösung. Methodengriffe sind cool, aber wahrscheinlich nicht so gut geeignet. – Guillaume

2

Eine der besten Bibliotheken dafür ist Netflix 'Hystrix. Ich bin mir nicht sicher, ob Sie solch ein schweres Heben brauchen. Es wird Ihnen Thread-Pool, Timeouts, Fallbacks, Überwachung, Laufzeitkonfiguration Änderungen, Kurzschluss, etc.

Grundsätzlich ist es eine Bibliothek, um Ihren Code vor Fehlern seiner Abhängigkeiten zu verteidigen.

+0

Hystrix sieht gut aus! Ich bewerte es gerade jetzt. Es bietet viel mehr als das, was der OP verlangt hat, aber das kann eine gute Sache sein ... – Guillaume

+0

Vielen Dank für die Erwähnung von Hystrix. War mir dessen nicht bewusst, aber es macht viel mehr als das, was ich brauche, und wir haben eine ziemlich strenge Richtlinie bezüglich des Hinzufügens von Bibliotheken. Prüfe es aber – user4132657

3

Ein Proxy kann Ihnen wahrscheinlich hier helfen. Das folgende Beispiel ist nicht getestet, aber sollten Sie eine Vorstellung davon, was Sie geschaffen könnte:

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