it-swarm.com.de

Ist die Rückgabe einer Ausnahme ein Anti-Muster?

Ich habe zwei einfache Methoden:

public void proceedWhenError() {
   Throwable exception = serviceUp();

   if (exception == null) {
      // do stuff
   } else {
      logger.debug("Exception happened, but it's alright.", exception)
      // do stuff
   }
}

public void doNotProceedWhenError() {
   Throwable exception = serviceUp();

   if (exception == null) {
      // do stuff
   } else {
      // do stuff
      throw new IllegalStateException("Oh, we cannot proceed. The service is not up.", exception)
   }
}

Die dritte Methode ist eine private Hilfsmethode:

private Throwable serviceUp() {
    try {
        service.connect();
        return null;
    catch(Exception e) {
       return e;
    }
}

Wir hatten ein kleines Gespräch mit einem Kollegen von mir über das hier verwendete Muster:

Rückgabe eines Exception (oder Throwable) Objekts aus der serviceUp() Methode .

Die erste Meinung:

Es ist ein Anti-Pattern, Exceptions zur Steuerung des Workflows zu verwenden, und wir sollten nur Boolean von .__ zurückgeben. serviceUp() und niemals das Exception-Objekt selbst. Das Argument lautet dass die Verwendung von Ausnahmen zur Steuerung des Workflows ein Anti-Pattern ist.

Die zweite Meinung:

Es ist in Ordnung, da wir uns später im .__ mit dem Objekt befassen müssen. zwei erste Methoden unterschiedlich und ob das Exception-Objekt zurückgegeben wird oder boolean ändert den Workflow überhaupt nicht

Halten Sie 1) oder 2) für richtig und insbesondere warum? Beachten Sie, dass es sich bei der Frage NUR um die Methode serviceUp() und deren Rückgabetyp - boolean vs Exception - Objekt handelt.

Hinweis: Ich frage nicht, ob Throwable- oder Exception-Objekte verwendet werden sollen.

19
Martin Linha

Es ist ein Anti-Pattern, Ausnahmen nur dann zu verwenden, wenn die Ausnahme in einer Ausnahmesituation ausgelöst wird*. Das Beenden einer Schleife durch Auslösen einer Ausnahme, wenn Sie das Ende einer Sammlung erreichen, ist beispielsweise ein Anti-Muster.

Die Steuerung des Flusses mit tatsächlichen Ausnahmen ist dagegen eine gute Anwendung von Ausnahmen. Wenn Ihre Methode auf eine Ausnahmesituation stößt, mit der sie nicht umgehen kann, sollte sie eine Ausnahme auslösen und den Fluss im Aufrufer an den Ausnahmebehandlungsblock weiterleiten.

Die Rückgabe eines "nackten" Exception-Objekts aus einer Methode, anstatt sie zu werfen, ist sicherlich kontraintuitiv. Wenn Sie dem Aufrufer die Ergebnisse einer Operation mitteilen müssen, ist es besser, ein Statusobjekt zu verwenden, das alle relevanten Informationen einschließt, einschließlich der Ausnahme:

public class CallStatus {
    private final Exception serviceException;
    private final boolean isSuccess;
    public static final CallStatus SUCCESS = new CallStatus(null, true);
    private CallStatus(Exception e, boolean s) {
        serviceException = e;
        isSuccess = s;
    }
    public boolean isSuccess() { return isSuccess; }
    public Exception getServiceException() { return serviceException; }
    public static CallStatus error(Exception e) {
        return new CallStatus(e, false);
    }
}

Nun würde der Anrufer CallStatus von serviceUp erhalten:

CallStatus status = serviceUp();
if (status.isSuccess()) {
    ... // Do something
} else {
    ... // Do something else
    Exception ex = status.getException();
}

Beachten Sie, dass der Konstruktor privat ist. serviceUp würde entweder CallStatus.SUCCESS zurückgeben oder CallStatus.error(myException) aufrufen.

* Was außergewöhnlich ist und was nicht außergewöhnlich ist, hängt stark vom Kontext ab. Nicht-numerische Daten verursachen beispielsweise eine Ausnahme in Scanners nextInt, da solche Daten als ungültig betrachtet werden. Dieselben genauen Daten verursachen jedoch keine Ausnahme in der hasNextInt-Methode, da sie vollkommen gültig sind.

30
dasblinkenlight

Die zweite Meinung ("Es ist in Ordnung") trifft nicht zu. Der Code ist nicht in Ordnung, da die Rückgabe von Ausnahmen statt deren Auslösen nicht wirklich idiomatisch ist.

Ich kaufe auch nicht die erste Meinung ("die Verwendung von Ausnahmen zur Steuerung des Workflows ist Anti-Muster"). service.connect() löst eine Exception aus und Sie müssen auf diese Exception reagieren - so dass diese {ist Flusssteuerung effektiv ist. Die Rückgabe einer boolean oder eines anderen Zustandsobjekts und die Verarbeitung dieses Objekts, anstatt eine Ausnahme zu behandeln, und zu denken, dass es sich nicht um einen Kontrollfluss handelt, der auf einer Ausnahme basiert, ist naiv. Ein weiterer Nachteil besteht darin, dass Sie die ursprüngliche Ausnahme nicht mehr haben, wenn Sie sich dafür entscheiden, eine Ausnahme erneut zu starten (in IllegalArgumentException eingeschlossen). Und das ist extrem schlecht, wenn Sie versuchen zu analysieren, was tatsächlich passiert ist.

Also würde ich den Klassiker machen:

  • Wirf die Ausnahme in serviceUp.
  • In Methoden, die serviceUp: .__ aufrufen.
    • try-catch, protokollieren Sie debug und schlucken Sie die Ausnahme, wenn Sie mit der Ausnahme fortfahren möchten.
    • try-catch und werfen Sie die in eine andere Ausnahme eingeschlossene Ausnahme erneut ein, um weitere Informationen zu erhalten, was passiert ist. Alternativ lassen Sie die ursprüngliche Ausnahme einfach über throws verbreiten, wenn Sie nichts Wesentliches hinzufügen können.

Es ist äußerst wichtig, die ursprüngliche Ausnahme nicht zu verlieren.

17
lexicore

Beides ist falsch

Es ist in Ordnung, da wir das Objekt anschließend in den beiden ersten Methoden unterschiedlich behandeln müssen und ob das Zurückgeben von Exception object oder boolean den Workflow überhaupt nicht verändert

Es ist nicht in Ordnung. Das Konzept der Ausnahmen bedeutet, dass sie auch in Ausnahmefällen geworfen werden. Sie sollen an dem Ort gefangen werden, an dem sie gehandhabt werden (oder zumindest nach einer lokalen Bereinigung/Protokollierung/usw. Wieder weggeworfen werden). Sie dürfen nicht so weitergegeben werden (dh im "Domain" -Code).

Die Leute werden verwirrt sein. Echte Bugs können es leicht kriechen - zum Beispiel, wenn es etwas Exception aus einer anderen Quelle als dem Netzwerk gibt; eine, mit der Sie nicht gerechnet haben, ist das wirklich schlimm (wie eine Ausnahme außerhalb der Grenzen, die Sie durch einen Programmierfehler erzeugt haben)?

Und neue Programmierer werden endlos verwirrt sein und/oder dieses Anti-Pattern an Orte kopieren, an die es einfach nicht gehört.

Zum Beispiel hat ein Kollege kürzlich eine recht komplizierte Schnittstelle implementiert (wie bei der Maschine-zu-Maschine-Schnittstelle, nicht Java interface), und er hat eine ähnliche Ausnahmebehandlung durchgeführt: das Konvertieren von Ausnahmen zu unbemerkt ignorierten Variationen (modulo einige Logmeldungen). Unnötig zu sagen, jede Ausnahme, die er eigentlich nicht erwartet hatte, hat das ganze Chaos auf die schlimmste vorstellbare Weise gebrochen ; das Gegenteil von schnell scheitern .

Es ist ein Antimuster, Ausnahmen zur Steuerung des Workflows zu verwenden, und wir sollten nur boolesche Werte von serviceUp () zurückgeben und niemals das Ausnahmeobjekt selbst. Das Argument ist, dass die Verwendung von Ausnahmen zur Steuerung des Workflows ein Anti-Pattern ist.

Ausnahmen steuern mit Sicherheit den Workflow in dem Aspekt, in dem sie ihn häufig abbrechen, oder leiten zu einer Fehlermeldung weiter, die dem Benutzer angezeigt wird. Es ist absolut möglich, dass ein Teil des Codes aufgrund einer Ausnahme deaktiviert wird. d.h. die Ausnahmebehandlung ist sicherlich an einer anderen Stelle als nur auf der obersten Kontrollebene zulässig.

Aber die Rückkehr von Ausnahmen ist in der Tat ein Anti-Muster. niemand erwartet das, es ist seltsam, es führt zu falschen Fehlern (es ist leicht, den Rückgabewert zu ignorieren) usw. usw.

Also, im Fall Ihrer serviceUp(), entweder machen Sie es void - es funktioniert entweder 99% der Zeit, oder wirft eine Ausnahme; oder mach es wahr boolean, indem du vollkommen akzeptierst, dass es irgendwo scheitern wird. Wenn Sie die Fehlermeldung weitergeben müssen, tun Sie dies als String oder speichern Sie sie irgendwo außerhalb des Weges oder so, aber verwenden Sie sie nicht als Rückgabewert, insbesondere nicht, wenn Sie beabsichtigen, throw es später noch einmal.

Einfache Standardlösung

Diese Lösung ist kürzer (weniger Zeilen, weniger Variablen, weniger if), einfacher, Moorstandard und macht genau das, was Sie wollten. Einfach zu pflegen, leicht zu verstehen.

public void proceedWhenError() {
   try {
      serviceUp();
      // do stuff (only when no exception)
   }
   catch (Exception exception) {
      logger.debug("Exception happened, but it's alright.", exception)
      // do stuff (only when exception)
   }
}

public void doNotProceedWhenError() {
   try {
      serviceUp();
      // do stuff (only when no exception)
   }
   catch (Exception exception) {
      // do stuff (only when exception)
      throw new IllegalStateException("Oh, we cannot proceed. The service is not up.", exception)
   }
}

private void serviceUp() {
    service.connect();
}
7
AnoE

Ich würde eine ServiceState zurückgeben, die beispielsweise RUNNING, WAITING, CLOSED sein kann. Die Methode würde getServiceState heißen.

enum ServiceState { RUNNING, WAITING, CLOSED; }

Ich habe noch nie die Methoden gesehen, die aufgrund der Ausführung eine Ausnahme zurückgeben. Wenn eine Methode den Wert zurückgibt, bedeutet dies für mich, dass die Methode ohne Probleme beendet wurde. Ich möchte das Ergebnis nicht abrufen und es nach Fehlern analysieren. Das Ergebnis selbst bedeutet, dass keine Fehler aufgetreten sind - alles lief wie geplant.

Andererseits muss ich, wenn die Methode eine Ausnahme auslöst, ein special - Objekt analysieren, um herauszufinden, was schief gelaufen ist. Ich analysiere das Ergebnis nicht, weil es kein Ergebnis gibt.

Ein Beispiel:

public void proceedWhenError() {
   final ServiceState state = getServiceState();

   if (state != ServiceState.RUNNING) {
      logger.debug("The service is not running, but it's alright.");
   }
   // do stuff
}

public void doNotProceedWhenError() {
   final ServiceState state = getServiceState();

   if (state != ServiceState.RUNNING) {
      throw new IllegalStateException("The service is not running...");
   }
   // do stuff
}

private ServiceState getServiceState() {
    try {
        service.connect();
        return ServiceState.RUNNING;
    catch(Exception e) {
        // determine the state by parsing the exception
        // and return it
        return getStateFromException(e);
    }
}

Wenn die vom Dienst geworfenen Ausnahmen wichtig sind und/oder an einem anderen Ort verarbeitet werden, kann er zusammen mit dem Status in einem ServiceResponse-Objekt gespeichert werden:

class ServiceResponse {

    private final ServiceState state;
    private final Exception exception;

    public ServiceResponse(ServiceState state, Exception exception) {
        this.state = state;
        this.exception = exception;
    }

    public static ServiceResponse of(ServiceState state) {
        return new ServiceResponse(state, null);
    }

    public static ServiceResponse of(Exception exception) {
        return new ServiceResponse(null, exception);
    }

    public ServiceState getState() {
        return state;
    }

    public Exception getException() {
        return exception;
    }

}

Mit ServiceResponse könnten diese Methoden nun so aussehen:

public void proceedWhenError() {
   final ServiceResponse response = getServiceResponse();

   final ServiceState state = response.getState();
   final Exception exception = response.getException();

   if (state != ServiceState.RUNNING) {
      logger.debug("The service is not running, but it's alright.", exception);
   }
   // do stuff
}

public void doNotProceedWhenError() {
   final ServiceResponse response = getServiceResponse();

   final ServiceState state = response.getState();
   final Exception exception = response.getException();

   if (state != ServiceState.RUNNING) {
      throw new IllegalStateException("The service is not running...", exception);
   }
   // do stuff
}

private ServiceResponse getServiceResponse() {
    try {
        service.connect();
        return ServiceResponse.of(ServiceState.RUNNING);
    catch(Exception e) {
        // or -> return ServiceResponse.of(e);
        return new ServiceResponse(getStateFromException(e), e);
    }
}
3
Andrew Tobilko

Die offensichtliche kognitive Dissonanz ist hier das Anti-Muster. Ein Leser wird sehen, dass Sie Ausnahmen für die Flusskontrolle verwenden, und ein Entwickler würde sofort versuchen, sie neu zu codieren, so dass dies nicht der Fall ist.

Mein Instinkt schlägt einen Ansatz vor wie:

// Use an action name instead of a question here because it IS an action.
private void bringServiceUp() throws Exception {

}

// Encapsulate both a success and a failure into the result.
class Result {
    final boolean success;
    final Exception failure;

    private Result(boolean success, Exception failure) {
        this.success = success;
        this.failure = failure;
    }

    Result(boolean success) {
        this(success, null);
    }

    Result(Exception exception) {
        this(false, exception);
    }

    public boolean wasSuccessful() {
        return success;
    }

    public Exception getFailure() {
        return failure;
    }
}

// No more cognitive dissonance.
private Result tryToBringServiceUp() {
    try {
        bringServiceUp();
    } catch (Exception e) {
        return new Result(e);
    }
    return new Result(true);
}

// Now these two are fine.
public void proceedWhenError() {
    Result result = tryToBringServiceUp();
    if (result.wasSuccessful()) {
        // do stuff
    } else {
        logger.debug("Exception happened, but it's alright.", result.getFailure());
        // do stuff
    }
}

public void doNotProceedWhenError() throws IllegalStateException {
    Result result = tryToBringServiceUp();
    if (result.wasSuccessful()) {
        // do stuff
    } else {
        // do stuff
        throw new IllegalStateException("Oh, we cannot proceed. The service is not up.", result.getFailure());
    }
}
1
OldCurmudgeon

Wenn der Aufrufer einer Methode erwartet, dass eine Operation erfolgreich ist oder fehlschlägt, und bereit ist, beide Fälle zu bearbeiten, sollte die Methode normalerweise über den Rückgabewert anzeigen, ob die Operation fehlgeschlagen ist. Wenn die Anrufer nicht bereit sind, Fehler sinnvoll zu behandeln, sollte die Methode, in der der Fehler aufgetreten ist, die Ausnahme auslösen, anstatt dass Anrufer dazu aufgefordert werden, Code hinzuzufügen.

Die eine Falte ist, dass einige Methoden einige Anrufer haben, die bereit sind, Fehler elegant zu behandeln, und andere, die dies nicht tun. Mein bevorzugter Ansatz wäre, dass solche Methoden einen optionalen Rückruf akzeptieren, der im Fehlerfall aufgerufen wird. Wenn kein Rückruf angegeben wird, sollte das Standardverhalten eine Ausnahme auslösen. Ein solcher Ansatz würde die Kosten für das Erstellen einer Ausnahme in Fällen sparen, in denen ein Anrufer bereit ist, einen Fehler zu behandeln, und gleichzeitig die Belastung für Anrufer minimiert, die dies nicht tun. Die größte Schwierigkeit bei einem solchen Entwurf ist die Entscheidung, welche Parameter ein solcher Rückruf annehmen soll, da das spätere Ändern solcher Parameter schwierig sein kann.

1
supercat

das Zurückgeben einer Ausnahme ist in der Tat ein Anti-Muster, da Ausnahmen für Fehler bei der Ausführung reserviert werden sollten, um nicht den Zustand des Dienstes zu beschreiben.

stellen Sie sich vor, wenn der Code von serviceUp() einen Fehler enthält, durch den NullPointerException ausgelöst wird. Stellen Sie sich nun vor, der Fehler befindet sich im Dienst und dieselbe NullPointerException wird von der connect() geworfen. 

Sehen Sie meinen Punkt?

Ein weiterer Grund ist die Änderung der Anforderungen. 

Derzeit hat der Dienst zwei Bedingungen: entweder nach oben oder nach unten.
Zur Zeit.

Morgen haben Sie drei Bedingungen für den Dienst: Auf, Ab. oder mit Warnungen funktionieren. Am Tag danach möchten Sie auch, dass die Methode Details zum Dienst in json zurückgibt.

1

Wie in früheren Kommentaren erwähnt, ist "Ausnahme ein Ereignis" das Ausnahmeobjekt, das wir erhalten, nur ein Detail des Ereignisses. Wenn eine Ausnahme im "catch" -Block gefangen und nicht erneut ausgelöst wird, ist das Ereignis beendetPost, dass Sie nur ein Detailobjekt haben, keine Ausnahme, obwohl das Objekt der Klasse exception/throwable ist.

Die Rückgabe des Exception-Objekts kann aufgrund der von ihm gehaltenen Details nützlich sein, fügt jedoch Mehrdeutigkeiten/Verwirrungen hinzu, da die aufrufende Methode nicht "Exception behandelt und den Fluss auf Ausnahmebasis steuert". Es werden lediglich Details eines zurückgegebenen Objekts verwendet, um Entscheidungen zu treffen.

Aus meiner Sicht ist es logischer und weniger verwirrend, ein auf der Ausnahme basierendes Boolean/Enum zurückzugeben, anstatt nur ein Exception-Objekt zurückzugeben. 

0
dev. K

Es gibt drei (idiomatische) Funktionen, mit denen Funktionen behandelt werden können, die zur Laufzeit fehlschlagen können.

1. Boolean zurückgeben

Die erste ist, boolean zurückzugeben. Dieser Stil ist in C- und C-Stil-APIs wie PHP weit verbreitet. Dies führt - beispielsweise in PHP - häufig zu folgendem Code:

if (xyz_parse($data) === FALSE)
    $error = xyz_last_error();

Die Nachteile dieses Verfahrens liegen auf der Hand, weshalb es immer mehr aus der Mode geraten ist, insbesondere in OOP - und Sprachen mit Ausnahmebehandlung.

2. Verwenden Sie ein Zwischenobjekt/Status/Ergebnisobjekt

Wenn Sie keine Ausnahmen verwenden, haben Sie in OOP -Sprachen noch die Möglichkeit, Objekte zurückzugeben, die den Zustand beschreiben.

Wenn Sie zum Beispiel Eingaben des Benutzers erhalten und die Eingabe überprüfen möchten, wissen Sie vorher, dass Sie möglicherweise Müll bekommen und dass das Ergebnis sehr oft nicht überprüft wird, und Sie könnten schreibe Code wie folgt:

ValidationResult result = parser.validate(data);
if (result.isValid())
    // business logic
else
    error = result.getvalidationError();

3. Verwenden Sie Ausnahmen

Java macht das, wie Sie es mit Sockets gezeigt haben. Die Begründung ist, dass das Erstellen einer Verbindung sollte erfolgreich ist und nur unter außergewöhnlichen Umständen fehlschlägt, die eine besondere Behandlung erfordern.

Es anwenden

In Ihrem Fall würde ich einfach die Ausnahme direkt werfen und sogar die Hilfsmethode verwerfen. Ihre Hilfsmethode hat einen schlechten Namen. Es ist nicht wirklich {Abfrage, ob der Dienst aktiv ist (wie der Name schon sagt), sondern stattdessen einfach Verbindungen.

Verwenden Sie (markierte) Ausnahmen

Nehmen wir an, das Verbinden ist das, was Ihre Methode tatsächlich tut. In diesem Fall nennen wir es passender connectToService und lassen die Ausnahme direkt auslösen:

public void connectToService() thows IOException {
    // yada yada some init stuff, whatever
    socket.connect();
}

public void proceedWhenError() {
   try {
        connectToService();
   } else {
      logger.debug("Exception happened, but it's alright.", exception)
      // do stuff
   }
}

public void doNotProceedWhenError() throws IllegalStateException {
    try  {
        connectToService();
        // do stuff
    }
    catch(IOException e) {
      throw new IllegalStateException("Oh, we cannot proceed. The service is not up.", exception)
   }
}

Verwenden Sie boolean

Andererseits könnte es sein, dass Ihre serviceUp (die noch schlecht benannt ist und besser als isServiceUp bezeichnet werden sollte) tatsächlich query tut, ob der Dienst ausgeführt wird oder nicht wurden von jemand anderem gestartet). In diesem Fall wäre die Verwendung eines Boolean die richtige Methode

public boolean isServiceUp {
    return ...; // query the service however you need
}

Dies bezieht sich natürlich auf die erste Lösung der Verwendung von Booleans und ist nicht wirklich hilfreich, wenn Sie auch wissen müssen, dass warum der Dienst nicht in Betrieb ist.

Verwenden Sie Zwischen-/Ergebnis-/Statusobjekte

Lassen Sie uns diesen begrenzten Ansatz verwerfen und mit einem Zwischenobjekt/Ergebnis/Zustandsobjekt überarbeiten:

class ServiceState {
    enum State { CONNECTED, WAITING, CLOSED; }

    State state;
    IOException exception;
    String additionalMessage;

    public ServiceState (State state, IOException ex, String msg) {
         [...] // ommitted for brevity
    }

   // getters ommitted for brevity
}

Die Hilfsfunktion würde getServiceState:

public ServiceState getServiceState() {
    // query the service state however you need & return
}

public void proceedWhenError() {
   ServiceState state = getServiceState();

   if (state.getState() == State.CONNECTED) {
      // do stuff
   } else {
      logger.debug("Exception happened, but it's alright.", state.getException())
      // do stuff
   }
 }

public void doNotProceedWhenError() {
    ServiceState state = getServiceState();

   if (state.getState() == State.CONNECTED) {
      // do stuff
   } else {
      // do stuff
      throw new IllegalStateException("Oh, we cannot proceed. The service is not up.", exception)
   }
}

Beachten Sie, dass Sie sich auch in proceedWhenError wiederholen. Der gesamte Code könnte vereinfacht werden:

public void proceedWhenError() {
   ServiceState state = getServiceState();

  if (state.getState() != State.CONNECTED) {
      logger.debug("Exception happened, but it's alright.", state.getException())

   }
 // do stuff
}

Es gibt eine Debatte darüber, wann der zweite Fall und wann der dritte Fall verwendet wird. Einige Leute glauben, dass Ausnahmen außergewöhnlich sein sollten und dass Sie nicht im Hinblick auf die Möglichkeit von Ausnahmen planen sollten, sondern eher die zweite Option verwenden wird. Das ist gut. Wir haben jedoch Ausnahmen in Java geprüft, daher sehe ich keinen Grund, sie nicht zu verwenden. Ich verwende geprüfte Ausnahmen Wenn die grundlegende Annahme ist, dass der Anruf erfolgreich sein soll (wie die Verwendung eines Sockets), ist ein Fehler jedoch möglich, und ich verwende die zweite Option, wenn es sehr unklar ist, ob der Anruf erfolgreich sein soll (wie das Überprüfen von Daten). Es gibt jedoch unterschiedliche Meinungen dazu.

Es hängt auch davon ab, was Ihre Hilfsmethode eigentlich tut. isServiceUp impliziert, dass es fragt einen Status ab, ändert es jedoch nicht. In diesem Fall ist die Rückgabe eines Zustandsobjekts offensichtlich die idiomatische Art, dies zu handhaben.

Ihre Implementierung zeigt jedoch, dass die Hilfsmethode (Verbindungen mit dem Dienst verbunden ist. In diesem Fall wäre es zumindest in Java die idiomatische Vorgehensweise, eine (geprüfte) Ausnahme auszulösen. Sie können sich jedoch auch mit einem Ergebnisobjekt rechtfertigen.

Es ist jedoch nicht ratsam, boolean zurückzugeben, da dies zu begrenzt ist. Wenn alle Sie wissen müssen, ob der Dienst ausgeführt wird oder nicht (und nicht an der Ursache interessiert ist), könnte eine solche Methode noch nützlich sein. obwohl (und könnte hinter den Kulissen als Hilfsmethode implementiert werden, die nur return getServiceState() == State.SUCCESS tut).


Es ist ein Anti-Pattern, um den Workflow mit Ausnahmen zu steuern

Mal sehen, was ist eine Ausnahme ist?

Definition: Eine Ausnahme ist ein Ereignis, das während der Ausführung eines Programms auftritt, dh unterbricht den normalen Ablauf der Programmanweisungen.

Quelle: https://docs.Oracle.com/javase/tutorial/essential/exceptions/definition.html

Die eigentliche Definition einer Ausnahme ist, dass es ein Ereignis ist, das disprupts (und somit den Workflow) des Programms ändert. Wenn wie definiert ein Anti-Pattern ist, muss das gesamte Sprachmerkmal an sich als Anti-Pattern betrachtet werden. Es gibt Leute, die do glauben, dass Ausnahmen schlecht sind und einigeeinige gültige Argumente dafür haben, aber im Allgemeinen werden Ausnahmen als nützliche Konstrukte betrachtet und als betrachtet nützliche Tools für außergewöhnliche Workflows.

Throwing Eine Ausnahme ist sicherlich kein Anti-Pattern. Rückgabe dagegen ist. Ja, Ihr Code ist, wie dargestellt, nicht idiomatisch.

0
Polygnome

Dies ist ein Anti-Muster:

catch(Exception e) {
   return e;
}

Die einzige vernünftige Entschuldigung für die Rückgabe von Throwable ist die Bereitstellung detaillierter Fehlerinformationen auf einem schnellen Weg, auf dem ein Fehler erwartet wird, ohne den Preis zu zahlen1 der Ausnahmebehandlung. Als Bonus ist es einfach, in eine ausgelöste Ausnahme zu konvertieren, wenn der Anrufer nicht weiß, wie er mit dieser bestimmten Situation umgehen soll.

In Ihrem Szenario wurden diese Kosten jedoch bereits bezahlt. Wenn Sie die Ausnahme für Ihren Anrufer abfangen, hat dies keinen Vorteil.


1Pedantisch gesehen sind die direkten Kosten für das Auffinden einer Ausnahme (das Finden eines passenden Handlers, das Abwickeln des Stapels) ziemlich niedrig oder müssen ohnehin durchgeführt werden. Der Großteil der Kosten für try/catch im Vergleich zu einem Rückgabewert sind zufällige Aktionen, wie das Erstellen einer Stack-Ablaufverfolgung. Ob nicht ausgelagerte Ausnahmeobjekte einen guten Speicherplatz für effiziente Fehlerdaten bieten, hängt also davon ab, ob diese Nebenarbeiten zum Zeitpunkt des Aufbaus oder zum Zeitpunkt des Abwurfs ausgeführt werden. Daher kann es für verschiedene verwaltete Plattformen unterschiedlich sein, ob die Rückgabe eines Ausnahmeobjekts sinnvoll ist.

0
Ben Voigt

Sie können nicht sagen, dass die 2) Meinung falsch ist, da der Workflow so ausgeführt wird, wie Sie möchten. Aus logischer Sicht läuft es wie gewünscht, also ist es korrekt.

Aber es ist eine wirklich seltsame und nicht empfehlenswerte Methode. Erstens, weil Exception nicht dafür vorgesehen ist (was bedeutet, dass Sie ein Anti-Pattern ausführen). Es ist ein spezielles Objekt, das entworfen wurde, um geworfen und gefangen zu werden. Es ist also seltsam, es eher als Design zu verwenden, es für die Rückgabe zu wählen und es zu fangen, wenn es verwendet wird. Darüber hinaus werden Sie (möglicherweise ist es vernachlässigbar), dass Sie mit einem Leistungsproblem konfrontiert werden. Statt einen einfachen Boolean als Flag zu verwenden, instanziieren Sie ein ganzes Objekt.

Schließlich ist es auch nicht zu empfehlen, dass function etwas zurückgibt, wenn das Ziel der Funktion darin besteht, etwas zu erhalten (was nicht Ihr Fall ist). Sie sollten es so umgestalten, dass es eine Funktion ist, die einen Dienst startet. Dann wird nichts zurückgegeben, da nicht aufgerufen wird, um etwas zu erhalten, und es wird eine Ausnahme ausgelöst, wenn ein Fehler aufgetreten ist. Und wenn Sie wissen möchten, ob Ihre Servicearbeit funktioniert, erstellen Sie eine Funktion, die Ihnen diese Informationen wie public boolean isServiceStarted() geben soll.

0
vincrichaud