So führen Sie eine Reihe von Methoden am besten aus, selbst wenn eine Ausnahme auftritt

7

In einem aktuellen Java-Projekt haben wir einen Code ähnlich dem folgenden Beispiel:

%Vor%

Wie Sie sehen können, werden mehrere verschiedene Methoden mit genau demselben Objekt aufgerufen und für jeden Aufruf wird dieselbe Ausnahme abgefangen und auf die gleiche Weise (oder in sehr ähnlicher Weise) behandelt. Die Ausnahme wird nicht erneut ausgelöst, sondern nur protokolliert und dann verworfen.

Der einzige Grund, warum es eine try-catch um jede einzelne Methode gibt, besteht darin, immer alle Methoden auszuführen, unabhängig davon, ob eine zuvor ausgeführte Methode fehlgeschlagen ist.

Ich mag den obigen Code überhaupt nicht. Es nimmt viel Platz in Anspruch, ist sehr repetitiv (besonders die Protokollierung im catch -Block; hier nicht dargestellt) und sieht einfach schlecht aus.

Ich kann mir andere Möglichkeiten vorstellen, diesen Code zu schreiben, aber ich mag sie auch nicht besonders. Folgende Optionen kamen mir in den Sinn:

Loop-Switch-Sequenz / Fallbeispiel

(Siehe Wikipedia oder Der tägliche WTF )

%Vor%

Das ist offensichtlich ein schlechter Code, sehr fehleranfällig und sieht amateurhaft aus.

Reflexion

Verwenden Sie reflection, um Method -Objekte für die aufzurufenden Methoden abzurufen und in einer Liste in der Reihenfolge zu speichern, in der sie ausgeführt werden sollen. Dann iteriere über diese Liste und rufe die Methode mit anObject als einzigen Parameter auf. Die Ausnahme wird innerhalb der Schleife behandelt.

Ich mag diesen Ansatz nicht, da Bugs (zum Beispiel Tippfehler in den Methodennamen) nur zur Laufzeit auftauchen und die Reflection-API etwas gesprächig ist.

Functor

Erstellen Sie eine Functor-Klasse wie folgt:

%Vor%

Erstellen Sie dann eine Liste von Functor -Objekten, die die Methoden aufrufen. So:

%Vor%

Später, iteriere diese Liste und rufe execute() für jedes Functor -Objekt auf. Ich kann mein Gefühl für diesen Ansatz mit zwei Worten zusammenfassen: Code bloat.

Da ich nicht alle vier Ansätze mag, möchte ich dieses Problem hier diskutieren. Was ist Ihrer Meinung nach der beste Ansatz? Wie haben Sie ähnliche Probleme in der Vergangenheit gelöst? Gibt es vielleicht eine viel einfachere Lösung, die ich komplett vermisst habe?

    
Tobias Müller 24.03.2009, 21:30
quelle

8 Antworten

6

Ich würde den Refactoring-Ansatz befürworten (oder " Warum sind wir überhaupt hierher gekommen? "):

Überlegen Sie, warum die einzelnen Methoden die Ausnahme auslösen können, nachdem sie "stuff" mit myObject ausgeführt haben, und dass diese Ausnahme dann ignoriert werden kann. Da die Ausnahme die Methode nicht enthält, muss sich myObject in einem unbekannten Status befinden.

Wenn es sicher ist, die Ausnahme zu ignorieren, muss es sicher der falsche Weg sein zu kommunizieren, dass bei jeder Methode etwas schief gelaufen ist.

Stattdessen ist es wahrscheinlich jede Methode, die sich bei einem Fehler anmelden muss. Wenn Sie keine statischen Protokollierer verwenden, können Sie jeder Methode einen Protokollierer übergeben.

    
PeterR 24.03.2009 21:48
quelle
5

Der Funktoransatz ist meiner Meinung nach der schönste - es ist nur eine Schande, dass Java keine schönere Möglichkeit hat, Schließungen oder Delegierte darzustellen. Das ist im Grunde, was Sie wirklich nach, und in C # (und vielen anderen Sprachen) wäre es trivial.

Sie können den physischen Bloat etwas reduzieren, indem Sie etwas wie folgt verwenden:

%Vor%

Die Leerräume, die hier kollabieren, sind zwar gegen die von Ihnen verwendete Stilvorlage, aber ich denke, das macht den Code leichter lesbar, da Sie das Fleisch leichter sehen können.

Beginnen Sie besser mit der Lobbyarbeit für Schließungen in Java 8;)

    
Jon Skeet 24.03.2009 21:37
quelle
3

Beim ersten Blick stimme ich PeterR zu: Wenn es sicher ist, die Ausnahme so einfach zu ignorieren, sollte diese Methode vielleicht überhaupt keine Ausnahme auslösen.

Wenn Sie jedoch sicher sind, dass Sie genau das wollen, sagen Sie, vielleicht arbeiten Sie mit Methoden aus einer Drittanbieter-Bibliothek, die darauf bestehen, bestimmte Ausnahmen zu werfen, würde ich den folgenden Ansatz wählen:

  1. Erstellen Sie eine Schnittstelle mit allen Methoden, die aufgerufen werden können:

    %Vor%
  2. Erstellen Sie eine Standardimplementierungsklasse für die entsprechenden Methoden dieser Methoden, indem Sie sie möglicherweise von einer anderen Stelle aus umgestalten:

    %Vor%
  3. Verwenden Sie eine Java Proxy Klasse zum Erstellen ein dynamischer Proxy für XyzOperations , der alle Methoden an DefaultXyzOperations delegiert hätte, aber eine zentralisierte Ausnahmebehandlung in seinem InvocationHandler . Ich habe das Folgende nicht kompiliert, aber es ist eine grundlegende Gliederung:

    %Vor%
  4. verwenden Sie von da an diese Proxy-Instanz und rufen Sie einfach die gewünschten Methoden

  5. auf

Alternativ können Sie auch AspectJ oder eine ähnliche AOP-Lösung verwenden, um alle Methoden von XyzOperations um Ratschläge zu ergänzen und das zu tun Ausnahmebehandlung dort.

Ob Sie lieber eine neue Abhängigkeit einführen oder Proxies manuell schreiben möchten, hängt von Ihren persönlichen Vorlieben ab und davon, wie viel Code Sie insgesamt benötigen.

    
javashlook 24.03.2009 22:16
quelle
2

Ich stimme PeterR zu. Es fällt mir schwer zu glauben, dass Sie die Ausführung von etwas wirklich fortsetzen möchten, nachdem eine Ausnahme ausgelöst wurde. Wenn Sie das tun, dann ist wahrscheinlich etwas Außergewöhnliches nicht passiert.

Anders ausgedrückt, Ausnahmen sollten nicht für die Protokollierung oder Ablaufsteuerung verwendet werden. Sie sollten nur verwendet werden, wenn etwas Außergewöhnliches passiert ist, dass der Code auf der Ebene, auf die die Ausnahme geworfen wurde, nicht behandelt werden kann.

Als solches würde ich die Protokollierungsnachrichten internalisieren und die ausgelösten Ausnahmen entfernen.

Ich denke zumindest, dass Sie zurückgehen müssen, um zu verstehen, was der Code zu tun versucht und welche Geschäftswerte oder Regeln implementiert werden. Wie PeterR gesagt hat, versuche zu verstehen, "warum sind wir überhaupt hierher gekommen?" Teil des Codes und was genau die Ausnahmen bedeuten.

    
Chris Johnston 24.03.2009 22:01
quelle
2

Sind die Methoden, die Sie anrufen, unter Ihrer Kontrolle? Wenn dies der Fall ist, kann die Rückgabe von Fehlercodes (oder Objekten) in diesem speziellen Fall zu einem besseren Gesamtdesign führen als die Verwendung von Ausnahmen:

%Vor%

mit

%Vor%

und

%Vor%

Dies scheint weniger wortreich, obwohl nicht trocken.

Alternativ verwenden Sie einen AOP-Mechanismus, um die Aufrufe von doSomething , doOtherThing und finallyDoYetSomethingCompletelyDifferent mit einem Around Advice abzufangen, der zuerst die Exception behandelt und dann verwirft. Kombinieren Sie das mit RuntimeExceptions und einem Pointcut, der auf einer schönen beschreibenden Annotation basiert, und Sie fangen perfekt ein, was eine Art versteckter Querschneiderei ist.

Ich gestehe, dass ich den Ansatz von Functor mag, tho.

BEARBEITEN : Sehen Sie Ihren Kommentar zu einer der Antworten. Ich würde wahrscheinlich in diesem Fall mit dem AOP-Ansatz gehen.

    
Nils Wloka 24.03.2009 22:13
quelle
1

Abgesehen von dem Refactoring-Problem scheint AspectJ (oder ähnlich) der einfachste Weg zu sein, diese Ausnahmen transparent zu erfassen / zu melden. Sie sollten in der Lage sein, das try / catch um die Methodenaufrufe herum zu weben, auch wenn Sie neue hinzufügen (der Codeblock wäre vermutlich ziemlich zerbrechlich, wenn jemand diesen Code ändert, ohne ihn vollständig zu verstehen) die Begründung dahinter)

    
Brian Agnew 24.03.2009 22:34
quelle
0

Wenn Sie die verschiedene Code-Stil-Route gehen, würde ich mit einem einfachen bleiben:

%Vor%

Update: Ich sehe nicht, wie man mit einer Syntax wie der Functor-Methode irgendeinen der involvierten Codes reduziert. Wie von Jon erwähnt, unterstützt Java keine einfache Syntax, um es weiter zu reduzieren. Wenn es c # gäbe, gibt es viele Variationen, die Sie tun können, und zwar um die Tatsache, dass es nicht viel zusätzliche Syntax gibt, um solche Methoden zu erstellen, zB einen Ausdruck wie actions.Add (() = & gt; doSomething (anObject) );

    
eglasius 25.03.2009 00:32
quelle
0

Lassen Sie mich etwas näher auf den Funktor-Ansatz eingehen ...

Abhängig von der Komplexität Ihrer App lohnt es sich manchmal, einen Teil Ihrer Geschäftslogik in eine conf-Datei zu verschieben, in der sie angemessener und kurz ausgedrückt werden kann. Auf diese Weise trennen Sie die technischen Details (Funktorerstellung / -aufruf, Ausnahmebehandlung) von der Geschäftslogik - die Definition welcher Methoden und in welcher Reihenfolge zu nennen sind.

In der einfachsten Form könnte es etwa so aussehen:

%Vor%

Dabei ist ActionX eine Klasse, die die Functor-Klasse (oder Schnittstelle) implementiert.

    
Bartosz Klimek 25.03.2009 12:10
quelle

Tags und Links