Leckt eine erneute Ausnahmebedingung eine Abstraktion?


12

Ich habe eine Schnittstellenmethode, die in der Dokumentation angibt, dass eine bestimmte Art von Ausnahme ausgelöst wird. Eine Implementierung dieser Methode verwendet etwas, das eine Ausnahme auslöst. Die interne Ausnahme wird abgefangen und die vom Schnittstellenvertrag deklarierte Ausnahme wird ausgelöst. Hier ist ein kleines Codebeispiel zur besseren Erklärung. Es ist in PHP geschrieben, aber es ist ziemlich einfach zu befolgen.

// in the interface

/**
 * @return This method returns a doohickey to use when you need to foo
 * @throws DoohickeyDisasterException
 */
public function getThatDoohickey();

// in the implementation

public function getThatDoohickey() {

    try {
        $SomethingInTheClass->doSomethingThatThrowsAnException();
    } catch (Exception $Exc) {
        throw new DoohickeyDisasterException('Message about doohickey failure');
    }

    // other code may return the doohickey

}

Ich benutze diese Methode, um zu verhindern, dass die Abstraktion austritt.

Meine Fragen sind: Würde die Weitergabe der ausgelösten internen Ausnahme als vorherige Ausnahme die Abstraktion lecken? Wenn nicht, wäre es angemessen, die Nachricht der vorherigen Ausnahme einfach wiederzuverwenden? Wenn die Abstraktion undicht wäre, könnten Sie eine Anleitung geben, warum Sie glauben, dass dies der Fall ist?

Zur Verdeutlichung würde meine Frage den Wechsel zur folgenden Codezeile beinhalten

throw new DoohickeyDisasterException($Exc->getMessage(), null, $Exc);

Ausnahmen sollten sich auf Ursachen beziehen, nicht darauf, wer sie wirft . Wenn Ihr Code ein haben kann file_not_found, sollte er ein werfen file_not_found_exception. Ausnahmen sollten nicht bibliotheksspezifisch sein.
Mooing Duck

Antworten:


11

Meine Fragen sind: Würde die Weitergabe der ausgelösten internen Ausnahme als vorherige Ausnahme die Abstraktion lecken? Wenn nicht, wäre es angemessen, die Nachricht der vorherigen Ausnahme einfach wiederzuverwenden?

Die Antwort lautet: "Es kommt darauf an".

Insbesondere hängt es von der Ausnahme ab und was es bedeutet. Im Wesentlichen bedeutet ein erneutes Auslösen, dass Sie dies zu Ihrer Benutzeroberfläche hinzufügen. Würden Sie das tun, wenn Sie den Code, den Sie aufrufen, selbst geschrieben haben, oder um zu vermeiden, dass die Ausnahme umbenannt wird?

Wenn Ihre Ausnahme im aufgerufenen Code eine Rückverfolgung bis zur eigentlichen Ursache enthält, kann dies zu Datenverlusten außerhalb der Abstraktion führen. Im Allgemeinen halte ich dies jedoch für akzeptabel, da die Ausnahme entweder vom Aufrufer behandelt wird (und es niemanden interessiert, wo sie auftritt) kam von) oder dem Benutzer angezeigt (und Sie möchten die Grundursache für das Debuggen kennen.)

Häufig ist es jedoch eine sehr vernünftige Wahl der Implementierung, nur die Ausnahme durchzulassen, und kein Abstraktionsproblem. Fragen Sie einfach: "Würde ich das werfen, wenn der von mir angerufene Code nicht stimmt?"


8

Streng genommen, wenn die ‚innere‘ Ausnahme etwas über das Innenleben einer Implementierung zeigt, Sie sind undicht. Technisch gesehen sollten Sie also immer alles abfangen und Ihren eigenen Ausnahmetyp auslösen.

ABER.

Nicht wenige wichtige Argumente kann gemacht werden , gegen diese. Vor allem:

  • Ausnahmen sind außergewöhnliche Dinge ; Sie verstoßen bereits in vielerlei Hinsicht gegen die Regeln des „normalen Betriebs“, sodass sie ebenso gut die Verkapselung und die Abstraktion auf Schnittstellenebene verletzen können.
  • Der Vorteil einer aussagekräftigen Stapelverfolgung und punktgenauer Fehlerinformationen überwiegt häufig die OOP-Korrektheit, solange Sie keine internen Programminformationen über die Benutzeroberflächenbarriere hinaus verlieren (dies wäre eine Offenlegung von Informationen).
  • Das Einschließen jeder inneren Ausnahme in einen geeigneten äußeren Ausnahmetyp kann zu vielen nutzlosen Boilerplate-Codes führen , die den gesamten Zweck von Ausnahmen zunichte machen (nämlich die Fehlerbehandlung implementieren, ohne den regulären Codefluss mit Boilerplate für die Fehlerbehandlung zu verschmutzen).

Das heißt, zu kontrollieren und Verpackung Ausnahmen oft tut Sinn machen, wenn nur zusätzliche Informationen zu Ihrer Protokolle hinzuzufügen, oder interne Informationen an den Endverbraucher undicht zu verhindern. Fehlerbehandlung ist immer noch eine Kunstform, und es ist schwierig, sie auf ein paar allgemeine Regeln zu reduzieren. Einige Ausnahmen sollten gesprudelt werden, andere nicht, und die Entscheidung hängt auch vom Kontext ab. Wenn Sie eine Benutzeroberfläche codieren, möchten Sie dem Benutzer nichts ungefiltert durchlassen. Wenn Sie jedoch eine Bibliothek codieren, die ein Netzwerkprotokoll implementiert, ist es wahrscheinlich das Richtige, bestimmte Ausnahmen zu übersprudeln (schließlich können Sie bei einem ausgefallenen Netzwerk nur wenig tun, um die Informationen zu verbessern oder wiederherzustellen und fortzufahren übersetzen NetworkDownExceptionnach FoobarNetworkDownExceptionist einfach bedeutungslos.


6

Was Sie hier tun, ist zuallererst, keine Ausnahme erneut auszulösen. Das erneute Auslösen würde bedeuten, buchstäblich dieselbe Ausnahme auszulösen:

try {
    $SomethingInTheClass->doSomethingThatThrowsAnException();
} catch (Exception $Exc) {
    throw $Exc;
}

Offensichtlich ist das ein bisschen sinnlos. Das erneute Auslösen ist für Situationen vorgesehen, in denen Sie einige Ressourcen freigeben oder die Ausnahme protokollieren oder was auch immer Sie sonst tun möchten, ohne die Ausnahme daran zu hindern, den Stapel in die Luft zu sprudeln.

Sie ersetzen bereits alle Ausnahmen, aus welchen Gründen auch immer, durch eine DoohickeyDisasterException. Welches kann oder kann nicht sein, was Sie vorhaben, zu tun. Aber ich würde vorschlagen, dass Sie jedes Mal, wenn Sie dies tun, auch tun sollten, was Sie vorschlagen, und die ursprüngliche Ausnahme als innere Ausnahme übergeben, nur für den Fall, dass sie im Stack-Trace nützlich ist.

Aber es geht nicht um undichte Abstraktionen, das ist ein anderes Problem.

Um die Frage zu beantworten, ob eine erneut ausgelöste Ausnahme (oder tatsächlich eine nicht erfasste Ausnahme) eine undichte Abstraktion sein kann, würde ich sagen, dass dies von Ihrem aufrufenden Code abhängt. Wenn für diesen Code Kenntnisse über das abstrahierte Framework erforderlich sind, handelt es sich um eine undichte Abstraktion. Wenn Sie dieses Framework durch ein anderes ersetzen, müssen Sie den Code außerhalb Ihrer Abstraktion ändern.

Wenn zum Beispiel DoohickeyDisasterException eine Klasse aus dem Framework ist und Ihr aufrufender Code so aussieht, dann haben Sie eine undichte Abstraktion:

try {
    $wrapper->getThatDoohickey();
} catch (DoohickeyDisasterException $Exc) {
    echo "The Doohickey is all wrong!";
    echo $Exc->getMessage();
    gotoAHappyPlaceAndSingHappySongs();
}

Wenn Sie Ihr Framework eines Drittanbieters durch ein anderes Framework ersetzen, das einen anderen Ausnahmenamen verwendet, müssen Sie alle diese Verweise suchen und ändern. Besser ist es, eine eigene Exception-Klasse zu erstellen und die Framework-Exception in diese einzubinden.

Wenn andererseits DoohickeyDisasterException von Ihnen stammt, dann verlieren Sie keine Abstraktionen, und Sie sollten sich mehr mit meinem ersten Punkt befassen.


2

Die Regel, die ich zu befolgen versuche, lautet: Wickeln Sie es ein, wenn Sie es verursacht haben. In ähnlicher Weise sollte ein Fehler, der aufgrund einer Laufzeitausnahme erzeugt wurde, eine Art DoHickeyException oder MyApplicationException sein.

Dies impliziert, dass, wenn die Ursache des Fehlers etwas außerhalb Ihres Codes ist und Sie nicht erwarten, dass er fehlschlägt, dieser ordnungsgemäß fehlschlägt und erneut ausgelöst wird. Wenn es sich um ein Problem mit Ihrem Code handelt, schließen Sie es in eine (möglicherweise) benutzerdefinierte Ausnahme ein.

Zum Beispiel, wenn eine Datei erwartet auf der Festplatte zu sein, oder ein Dienst voraussichtlich zur Verfügung steht, damit umgeht dann anmutig und wieder werfen die Fehler , so dass der Verbraucher Ihres Codes kann herausfinden , warum die Ressource nicht verfügbar ist. Wenn Sie für einen Typ zu viele Daten erstellt haben, ist es Ihr Fehler, sie umzubrechen und zu werfen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.