Ist es empfehlenswert, eine aktivierte Ausnahme abzufangen und eine RuntimeException auszulösen?

70

Ich habe einen Code eines Kollegen gelesen und festgestellt, dass er häufig verschiedene Ausnahmen abfängt und stattdessen immer eine 'RuntimeException' auslöst. Ich fand das immer sehr schlecht. Liege ich falsch?

RoflcoptrException
quelle
17
"Der Preis für geprüfte Ausnahmen ist eine Verletzung des Open / Closed-Prinzips. Wenn Sie eine geprüfte Ausnahme von einer Methode in Ihrem Code auslösen und der Fang drei Ebenen höher ist, müssen Sie diese Ausnahme in der Signatur jeder Methode zwischen Ihnen und dem Fang deklarieren Dies bedeutet, dass eine Änderung auf einer niedrigen Ebene der Software Signaturänderungen auf vielen höheren Ebenen erzwingen kann. " —Robert C. Martin, «Clean Code», Seite 107
Songo
6
Es ist interessant zu bemerken, dass Jim Waldo gegen ungeprüfte Ausnahmen in "Java: The Good Parts" protestiert . Wir haben es erst vor 6 Jahren in unserer Kanne gelesen, als es herauskam und es schien ein guter Rat zu sein! Mit der funktionalen Programmierung sind überprüfte Ausnahmen nun völlig unhandlich. Sprachen wie Scala und Kotlin haben sie nicht einmal. Ich habe damit begonnen, auch aktivierte Ausnahmen einzuschließen.
GlenPeterson
@ GlenPeterson Sie haben auch den Ratschlag in FP, Ausführungen ganz zu vermeiden und stattdessen Summentypen zu verwenden
jk.
Es gibt auch den offensichtlichen Fall von funktionalen Schnittstellen: die eingebauten funktionalen Schnittstellen (dh Function, Predicateusw.) haben werfen Klauseln nicht parametrisiert. Dies bedeutet , dass Sie brauchen zu fangen, wickeln, und rethrow alle geprüften Ausnahmen in der inneren Schleife von jedem Stream () Methoden. Das an und für sich gibt mir den Ausschlag, ob geprüfte Ausnahmen eine schlechte Idee sind.
Joel Cornett
Es ist nichts Falsches daran, benutzerdefinierte Unterklassen von RuntimeException zu erstellen, um die Bedeutung durch Ihre Ausnahme zu kommunizieren.
Joel Cornett

Antworten:

55

Ich kenne nicht genug Kontext, um zu wissen, ob Ihr Kollege etwas falsch macht oder nicht, deshalb werde ich im Allgemeinen darüber streiten.

Ich denke nicht, dass es immer eine falsche Praxis ist, geprüfte Ausnahmen in eine Art Laufzeitausnahme umzuwandeln . Überprüfte Ausnahmen werden häufig von Entwicklern missbraucht und missbraucht .

Es ist sehr einfach, geprüfte Ausnahmen zu verwenden, wenn sie nicht verwendet werden sollen (nicht behebbare Bedingungen oder sogar Kontrollfluss). Insbesondere wenn eine aktivierte Ausnahme für Bedingungen verwendet wird, unter denen der Aufrufer keine Wiederherstellung durchführen kann, ist es meines Erachtens gerechtfertigt, diese Ausnahme in eine Laufzeitausnahme mit einer hilfreichen Nachricht / einem hilfreichen Status umzuwandeln. Leider neigen sie in vielen Fällen dazu, einen leeren Fangblock zu haben, was eines der schlimmsten Dinge ist, die Sie tun können. Das Debuggen eines solchen Problems ist eines der größten Probleme, denen ein Entwickler begegnen kann.

Wenn Sie also der Meinung sind, dass es sich um eine wiederherstellbare Bedingung handelt, sollte diese entsprechend behandelt und die Ausnahme nicht in eine Laufzeitausnahme umgewandelt werden. Wenn eine aktivierte Ausnahme für nicht behebbare Bedingungen verwendet wird, ist die Umwandlung in eine Laufzeitausnahme gerechtfertigt .

c_maker
quelle
17
In den meisten realen Anwendungen gibt es nur sehr wenige nicht behebbare Zustände. Es gibt fast immer eine Ebene, auf der Sie sagen können und sollten: "OK, diese Aktion ist fehlgeschlagen. Wir zeigen / protokollieren eine nette Fehlermeldung und fahren mit / warten auf die nächste" fort.
Michael Borgwardt
6
Das ist wahr, @MichaelBorgwardt, aber der Ort für diese Art der Behandlung befindet sich oft auf der höchsten Ebene der Anwendung. Wenn ich also sehe, dass Entwickler Ausnahmen auf niedrigeren Ebenen "behandeln", ist es normalerweise einfach, ihre Behandlung zu entfernen und die Ausnahme einfach zu durchsickern aufwärts. Ein Webframework wie JSF fängt beispielsweise Ausnahmen auf höchster Ebene ab, druckt Protokollnachrichten und setzt die Verarbeitung anderer Anforderungen fort (die Standardbehandlung ist jedoch nur ein Beispiel).
DavidS
40

Es kann gut sein . Bitte lesen Sie:

http://onjava.com/pub/a/onjava/2003/11/19/exceptions.html

Meistens kann der Client-Code nichts gegen SQLExceptions unternehmen. Zögern Sie nicht, sie in ungeprüfte Ausnahmen umzuwandeln. Betrachten Sie den folgenden Code:

public void dataAccessCode(){
  try{
      ..some code that throws SQLException
  }catch(SQLException ex){
      ex.printStacktrace();
  }
} 

Dieser catch-Block unterdrückt nur die Ausnahme und führt nichts aus. Die Rechtfertigung ist, dass mein Client nichts gegen eine SQLException unternehmen kann. Wie wäre es damit wie folgt umzugehen?

public void dataAccessCode(){
   try{
       ..some code that throws SQLException
   }catch(SQLException ex){
       throw new RuntimeException(ex);
   }
} 

Dadurch wird SQLException in RuntimeException konvertiert. Wenn SQLException auftritt, löst die catch-Klausel eine neue RuntimeException aus. Der Ausführungsthread wird angehalten und die Ausnahme gemeldet. Ich beschädige jedoch nicht die Business-Objektschicht mit unnötiger Ausnahmebehandlung, insbesondere, weil damit keine SQLException behoben werden kann. Wenn mein Fang die Root-Ausnahmebedingung benötigt, kann ich die Methode getCause () verwenden, die in allen Ausnahmeklassen ab JDK1.4 verfügbar ist.

Es hilft nicht, geprüfte Ausnahmen auszulösen und sich nicht davon erholen zu können.

Einige Leute denken sogar, dass aktivierte Ausnahmen überhaupt nicht verwendet werden sollten. Siehe http://www.ibm.com/developerworks/java/library/j-jtp05254/index.html

Kürzlich haben mehrere angesehene Experten, darunter Bruce Eckel und Rod Johnson, öffentlich erklärt, dass sie der orthodoxen Position zu geprüften Ausnahmen zwar ursprünglich uneingeschränkt zustimmten, sie jedoch zu dem Schluss gekommen sind, dass die ausschließliche Verwendung geprüfter Ausnahmen keine so gute Idee ist wie sie Es tauchte zunächst auf, und diese überprüften Ausnahmen haben sich bei vielen großen Projekten zu einer erheblichen Problemquelle entwickelt. Eckel ist extremer und schlägt vor, alle Ausnahmen zu deaktivieren. Johnsons Ansicht ist konservativer, deutet aber immer noch darauf hin, dass die orthodoxe Präferenz für geprüfte Ausnahmen übertrieben ist. (Es ist erwähnenswert, dass die C # -Architekten, die mit ziemlicher Sicherheit viel Erfahrung mit Java-Technologie hatten, aktivierte Ausnahmen aus dem Sprachentwurf ausließen und alle Ausnahmen deaktiviert haben.

Auch vom selben Link:

Die Entscheidung, ungeprüfte Ausnahmen zu verwenden, ist kompliziert und es ist klar, dass es keine offensichtliche Antwort gibt. Der Sun-Rat ist, sie für nichts zu verwenden, der C # -Ansatz (dem Eckel und andere zustimmen) ist, sie für alles zu verwenden. Andere sagen: "Es gibt einen Mittelweg."

mrmuggles
quelle
13

Nein, du liegst nicht falsch. Seine Praxis ist extrem fehlgeleitet. Sie sollten eine Ausnahme auslösen, die das Problem erfasst, das sie verursacht hat. RunTimeException ist umfassend und übergreifend. Es sollte eine NullPointerException, eine ArgumentException usw. sein. Was auch immer genau beschreibt, was schief gelaufen ist. Dies bietet die Möglichkeit, Probleme, die Sie behandeln sollten, zu unterscheiden und das Programm im Vergleich zu Fehlern überleben zu lassen, bei denen es sich um ein Szenario handeln sollte, bei dem "Nicht bestanden" handelt. Was er tut, ist nur geringfügig besser als "On Error Resume Next", es sei denn, in den in der Frage angegebenen Informationen fehlt etwas.

Rig
quelle
1
Danke für den Tipp. Und was ist, wenn er eine benutzerdefinierte Ausnahme auslöst, die er implementiert hat und die direkt von RuntimeException erbt?
RoflcoptrException
27
@ Gary Buyn: Viele Leute denken, dass geprüfte Ausnahmen ein fehlgeschlagenes Sprachdesign-Experiment sind und sie sollten sparsam und nicht aus Gewohnheit verwendet werden.
Michael Borgwardt
7
@ Gary Buyn: Hier ist ein Artikel, der die Debatte ziemlich gut umreißt: ibm.com/developerworks/java/library/j-jtp05254/index.html Beachten Sie auch, dass über 15 Jahre nachdem Java diese Funktion eingeführt hat, keine andere Sprache sie übernommen hat. und C ++ hat eine ähnliche Funktion verworfen.
Michael Borgwardt
7
@c_maker: Eigentlich wirbt Bloch hauptsächlich für die orthodoxe Sichtweise, und in Ihrem Kommentar geht es anscheinend hauptsächlich darum, mehr Ausnahmen zu verwenden, Punkt. Meiner Ansicht nach ist der einzige gültige Grund, eine aktivierte Ausnahme zu verwenden, eine Bedingung, von der Sie erwarten, dass alle Anrufer sie sofort bearbeiten.
Michael Borgwardt
14
Durch unnötiges Auslösen von aktivierten Ausnahmen wird die Kapselung verletzt. Was tun Sie, wenn eine einfache Methode wie 'getAccounts ()' eine 'SQLException', 'NullPointerException' oder 'FileNotFoundException' auslöst? Können Sie damit umgehen? Sie werden es wahrscheinlich nur "fangen (Ausnahme e) {}". Außerdem diese Ausnahmen - seine Implementierung spezifisch! Es sollte nicht Vertragsbestandteil sein! Sie müssen lediglich wissen, dass ein Fehler aufgetreten ist . Und wenn sich die Implementierung ändert? Plötzlich muss sich alles ändern, da die Methode nicht mehr 'SQLException', sondern 'ParseXMLException' auslöst!
KL
8

Es hängt davon ab, ob.

Diese Praxis kann sogar weise sein . Es gibt viele Situationen (zum Beispiel bei der Webentwicklung), in denen Sie im Ausnahmefall nichts tun können (weil Sie zum Beispiel keine inkonsistente Datenbank aus Ihrem Code reparieren können :-), dies kann nur der Entwickler tun). In diesen Situationen ist es ratsam, die ausgelöste Ausnahme in eine Laufzeitausnahme zu verpacken und sie erneut zu werfen. Dann können Sie alle diese Ausnahmen in einer Ausnahmebehandlungsebene abfangen, den Fehler protokollieren und dem Benutzer einen netten lokalisierten Fehlercode + eine Meldung anzeigen.

Auf der anderen Seite , wenn die Ausnahme nicht Laufzeit ist (wird überprüft), der Entwickler der API gibt an , dass diese Ausnahme auflösbar ist und soll repariert werden. Wenn es möglich ist, sollten Sie es auf jeden Fall tun.

Die andere Lösung könnte darin bestehen, diese aktivierte Ausnahme erneut in die aufrufende Ebene zu werfen. Wenn Sie sie jedoch nicht lösen konnten, wo die Ausnahme aufgetreten ist, können Sie sie wahrscheinlich auch hier nicht lösen ...

malejpavouk
quelle
Sie hoffen, dass der Entwickler der API wusste, was er tat, und überprüfte Ausnahmen gut verwendete. Ich habe angefangen, APIs zu sehen, die es bevorzugen, Laufzeitausnahmen auszulösen, während sie es auch dokumentieren, so dass der Client die Option hat, es abzufangen, wenn er möchte.
c_maker
Eine Methode, die eine Ausnahme auslöst, kann im Allgemeinen nicht wissen, ob ein Aufrufer sie möglicherweise wiederherstellt. Andererseits würde ich vorschlagen, dass eine Methode eine überprüfte Ausnahme, die von einer inneren Methode ausgelöst wird, nur dann entweichen lässt, wenn bekannt ist, warum die innere Methode die Ausnahme ausgelöst hat und der Grund mit der API der äußeren Methode übereinstimmt. Wenn eine innere Methode eine geprüfte Ausnahme unerwartet auslöst und sie als geprüfte Ausnahme in die Luft sprudeln lässt, wird der Aufrufer möglicherweise falsch darüber informiert, was passiert ist.
Supercat
2
Vielen Dank für die Erwähnung des exception handling layer- zB in einer Webapp, einem Filter.
Jake Toronto
5

Ich würde gerne Kommentare dazu erhalten, aber ich finde, dass es Zeiten gibt, in denen dies nicht unbedingt eine schlechte Praxis ist. (Oder schrecklich schlecht). Aber vielleicht irre ich mich.

Häufig löst eine von Ihnen verwendete API eine Ausnahme aus, die Sie sich nicht vorstellen können, tatsächlich in Ihrem speziellen Anwendungsfall ausgelöst zu werden. In diesem Fall ist es völlig in Ordnung, eine RuntimeException mit der festgestellten Ausnahme als Ursache auszulösen. Wenn diese Ausnahme ausgelöst wird, ist dies wahrscheinlich die Ursache für Programmierfehler und liegt nicht innerhalb der Grenzen für die korrekte Spezifikation.

Vorausgesetzt, die RuntimeException wird später nicht abgefangen und ignoriert, ist sie nicht annähernd ein OnErrorResumeNext.

OnErrorResumeNext tritt auf, wenn eine Ausnahme abgefangen und einfach ignoriert oder nur ausgedruckt wird. Dies ist in fast allen Fällen eine schrecklich schlechte Praxis.

user606723
quelle
Dies kann am oberen Rand des Aufrufbaums der Fall sein. Sie können nur versuchen, eine ordnungsgemäße Wiederherstellung durchzuführen, und wenn Sie wissen, dass der spezifische Fehler nicht wirklich hilfreich ist. In diesem Fall müssen Sie möglicherweise den Fehler aufzeichnen und fortfahren (nächsten Datensatz verarbeiten, Benutzer über das Auftreten eines Fehlers informieren usw.). Ansonsten nein. Sie sollten Ausnahmen immer so nah wie möglich am Fehler behandeln und sie nicht als weißen Elefanten für den nächsten Handler einpacken.
Michael K
@MichaelK Das Problem ist "so nah am Fehler, wie es praktisch ist", bedeutet in der Praxis oft "durch mehrere dazwischen liegende Ebenen, die sich Ihrer direkten Kontrolle entziehen". Wenn meine Klasse beispielsweise eine bestimmte Schnittstelle implementieren muss, sind mir die Hände gebunden. Das kann beliebig tief im Aufrufbaum passieren. Selbst wenn die Schnittstellen unter meiner Kontrolle stehen, kann das Hinzufügen von Throws-Deklarationen die Abstraktion undicht machen, wenn nur eine begrenzte Anzahl konkreter Implementierungen vorhanden ist, die möglicherweise Throws auslösen können. Jeden Kunden die Kosten für die Implementierungsdetails einiger weniger zahlen zu lassen, ist kein großer Kompromiss zwischen Design und IMO.
Tim Seguine
4

TL; DR

Prämisse

  • Laufzeitausnahmen sollten ausgelöst werden, wenn der Fehler nicht behebbar ist: Wenn der Fehler im Code enthalten ist und nicht vom externen Status abhängt (daher würde die Wiederherstellung den Code korrigieren).
  • Überprüfte Ausnahmen sollten ausgelöst werden, wenn der Code korrekt ist, der externe Status jedoch nicht den Erwartungen entspricht: Keine Netzwerkverbindung, Datei nicht gefunden oder beschädigt usw.

Fazit

Wir können eine aktivierte Ausnahme als Laufzeitausnahme erneut auslösen, wenn der Propagierungs- oder Schnittstellencode davon ausgeht, dass die zugrunde liegende Implementierung vom externen Status abhängt, wenn dies eindeutig nicht der Fall ist.


In diesem Abschnitt wird erläutert, wann eine der Ausnahmen ausgelöst werden soll. Sie können zur nächsten horizontalen Leiste springen, wenn Sie nur eine ausführlichere Erklärung für die Schlussfolgerung lesen möchten.

Wann ist es angebracht, eine Laufzeitausnahme auszulösen? Sie lösen eine Laufzeitausnahme aus, wenn klar ist, dass der Code nicht korrekt ist und die Wiederherstellung durch Ändern des Codes angemessen ist.

Beispielsweise ist es angebracht, eine Laufzeitausnahme für Folgendes auszulösen:

float nan = 1/0;

Dadurch wird eine Laufzeitausnahme für die Division durch Null ausgelöst. Dies ist angemessen, da der Code fehlerhaft ist.

Oder zum Beispiel, hier ist ein Teil von HashMap's Konstruktor:

public HashMap(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: " +
                loadFactor);
    // more irrelevant code...
}

Um die anfängliche Kapazität oder den anfänglichen Auslastungsfaktor zu beheben, müssen Sie den Code bearbeiten, um sicherzustellen, dass die richtigen Werte übergeben werden. Dies hängt nicht davon ab, ob ein weit entfernter Server in Betrieb ist. eine Datei oder ein anderes Programm. Der Konstruktor, der mit ungültigen Argumenten aufgerufen wird, hängt von der Richtigkeit des aufrufenden Codes ab, sei es eine falsche Berechnung, die zu ungültigen Parametern oder einem unangemessenen Ablauf führte, bei dem ein Fehler übersehen wurde.

Wann ist es angebracht, eine aktivierte Ausnahme auszulösen? Sie werfen eine geprüfte Ausnahme , wenn das Problem ist ohne Änderung des Codes erstattungsfähig. Anders ausgedrückt, Sie lösen eine aktivierte Ausnahme aus, wenn der Fehler mit dem Status zusammenhängt, während der Code korrekt ist.

Jetzt kann das Wort "Wiederherstellen" hier schwierig sein. Dies kann bedeuten, dass Sie einen anderen Weg finden, um das Ziel zu erreichen: Wenn der Server beispielsweise nicht antwortet, sollten Sie den nächsten Server versuchen. Wenn diese Art der Wiederherstellung für Ihren Fall möglich ist, ist das großartig, aber das ist nicht das einzige, was Wiederherstellung bedeutet - Wiederherstellung könnte einfach darin bestehen, dem Benutzer einen Fehlerdialog anzuzeigen, der erklärt, was passiert ist, oder wenn es sich um eine Serveranwendung handelt, könnte dies der Fall sein Senden Sie eine E-Mail an den Administrator, oder protokollieren Sie den Fehler lediglich angemessen und präzise.

Nehmen wir das Beispiel, das in der Antwort von mrmuggles erwähnt wurde:

public void dataAccessCode(){
   try{
       ..some code that throws SQLException
   }catch(SQLException ex){
       throw new RuntimeException(ex);
   }
}

Dies ist nicht der richtige Weg, um die aktivierte Ausnahme zu behandeln. Die bloße Unfähigkeit, die Ausnahme im Geltungsbereich dieser Methode zu behandeln, bedeutet nicht, dass die App abstürzen sollte. Stattdessen ist es angebracht, es in einem höheren Bereich wie folgt zu verbreiten:

public Data dataAccessCode() throws SQLException {
    // some code that communicates with the database
}

Was die Möglichkeit der Wiederherstellung durch den Anrufer ermöglicht:

public void loadDataAndShowUi() {
    try {
        Data data = dataAccessCode();
        showUiForData(data);
    } catch(SQLException e) {
        // Recover by showing an error alert dialog
        showCantLoadDataErrorDialog();
    }
}

Überprüfte Ausnahmen sind ein statisches Analysetool. Sie machen einem Programmierer klar, was bei einem bestimmten Aufruf schief gehen kann, ohne dass er die Implementierung erlernen oder einen Test- und Fehlerprozess durchlaufen muss. Auf diese Weise kann auf einfache Weise sichergestellt werden, dass keine Teile des Fehlerflusses ignoriert werden. Das erneute Auslösen einer aktivierten Ausnahme als Laufzeitausnahme wirkt sich auf diese arbeitssparende statische Analysefunktion aus.

Es ist auch erwähnenswert, dass die aufrufende Schicht einen besseren Kontext für das größere Schema der Dinge aufweist, wie oben gezeigt wurde. Es könnte viele Ursachen dafür geben, dataAccessCodedass angerufen werden würde, der konkrete Grund für den Anruf ist nur für den Anrufer sichtbar - somit ist es in der Lage, eine bessere Entscheidung bei der korrekten Wiederherstellung nach einem Ausfall zu treffen.

Nachdem wir diese Unterscheidung getroffen haben, können wir ableiten, wann es in Ordnung ist, eine aktivierte Ausnahme erneut als Laufzeitausnahme zu werfen.


Wann ist es in Anbetracht des oben Gesagten angebracht, eine aktivierte Ausnahme erneut als RuntimeException auszugeben? Wenn der von Ihnen verwendete Code vom externen Status abhängig ist, können Sie jedoch eindeutig behaupten, dass er nicht vom externen Status abhängt.

Folgendes berücksichtigen:

StringReader sr = new StringReader("{\"test\":\"test\"}");
try {
    doesSomethingWithReader(sr); // calls #read, so propagates IOException
} catch (IOException e) {
    throw new IllegalStateException(e);
}

In diesem Beispiel wird der Code weitergegeben, IOExceptionda die API von Readerfür den Zugriff auf den externen Status ausgelegt ist. Wir wissen jedoch, dass die StringReaderImplementierung nicht auf den externen Status zugreift. In diesem Bereich, in dem wir mit Sicherheit behaupten können, dass die an dem Aufruf beteiligten Teile nicht auf E / A oder einen anderen externen Zustand zugreifen, können wir die Ausnahme sicher als Laufzeitausnahme zurückwerfen, ohne Kollegen zu überraschen, die sich unserer Implementierung nicht bewusst sind (und dies möglicherweise tun) unter der Annahme, dass der IO-Zugangscode einen IOException) auslöst .


Der Grund für die strikte Überprüfung von externen zustandsabhängigen Ausnahmen ist, dass sie nicht deterministisch sind (im Gegensatz zu logikabhängigen Ausnahmen, die vorhersehbar jedes Mal für eine Version des Codes reproduziert werden). Wenn Sie beispielsweise versuchen, durch 0 zu dividieren, wird immer eine Ausnahme erzeugt. Wenn Sie nicht durch 0 dividieren, wird niemals eine Ausnahme erzeugt, und Sie müssen diesen Ausnahmefall nicht behandeln, da dies niemals passieren wird. Wenn Sie jedoch einmal auf eine Datei zugreifen, bedeutet dies nicht, dass Sie das nächste Mal erfolgreich sind - der Benutzer hat möglicherweise die Berechtigungen geändert, ein anderer Prozess hat sie möglicherweise gelöscht oder geändert. Sie müssen also immer mit diesem Ausnahmefall fertig werden, oder Sie haben wahrscheinlich einen Fehler.

Ben Barkay
quelle
2

Für eigenständige Anwendungen. Wenn Sie wissen, dass Ihre Anwendung die Ausnahme nicht verarbeiten kann, können Sie anstelle der aktivierten RuntimeException Error auslösen, die Anwendung abstürzen lassen, auf Fehlerberichte hoffen und die Anwendung reparieren. (Siehe die Antwort von mrmuggles für eine eingehendere Diskussion der Vor- und Nachteile von aktiviert und deaktiviert .)

Kasper van den Berg
quelle
2

Dies ist in vielen Frameworks üblich. ZB Hibernatemacht genau das. Die Idee ist, dass die APIs für den Client nicht aufdringlich sein sollten und Exceptionaufdringlich sind, da Sie explizit Code schreiben müssen, um sie an der Stelle zu verarbeiten, an der Sie die API aufrufen. Aber dieser Ort ist möglicherweise nicht der richtige Ort, um die Ausnahme überhaupt zu behandeln.
Um ehrlich zu sein, ist dies ein "heißes" Thema und viele streiten sich, so dass ich keine Seite nehmen werde, aber ich werde sagen, dass das, was Ihr Freund tut / vorschlägt, nicht ungewöhnlich oder ungewöhnlich ist.

user10326
quelle
1

Die ganze "geprüfte Ausnahme" ist eine schlechte Idee.

Strukturierte Programmierung ermöglicht nur die Weitergabe von Informationen zwischen Funktionen (oder, in Java-Sprache, Methoden), wenn diese "in der Nähe" sind. Genauer gesagt, Informationen können sich nur auf zwei Arten über Funktionen hinweg bewegen:

  1. Von einem Anrufer zu einem Angerufenen über die Weitergabe von Argumenten.

  2. Vom Angerufenen zum Anrufer als Rückgabewert.

Das ist von Grund auf gut. Auf diese Weise können Sie lokal über Ihren Code nachdenken: Wenn Sie einen Teil Ihres Programms verstehen oder ändern müssen, müssen Sie sich nur diesen Teil und andere "nahe" liegende Teile ansehen.

Unter bestimmten Umständen ist es jedoch erforderlich, Informationen an eine "entfernte" Funktion zu senden, ohne dass es jemand in der Mitte "weiß". Genau hier müssen Ausnahmen verwendet werden. Eine Ausnahme ist eine geheime Nachricht, die von einem Auslöser (unabhängig davon, welcher Teil Ihres Codes eine throwAnweisung enthält) an einen Handler gesendet wird (unabhängig davon, welcher Teil Ihres Codes einen catchBlock enthält, der mit der Ausnahme kompatibel ist, die thrown war).

Überprüfte Ausnahmen zerstören die Geheimhaltung des Mechanismus und damit den eigentlichen Grund für seine Existenz. Wenn eine Funktion es sich leisten kann, ihren Aufrufer eine Information "wissen" zu lassen, senden Sie diese Information einfach direkt als Teil des Rückgabewerts.

Pyon
quelle
Es ist zu erwähnen, dass diese Art von Problem in Fällen, in denen eine Methode eine Funktion ausführt, die von ihrem Aufrufer bereitgestellt wird, wirklich Chaos anrichten kann. Der Autor der Methode, die die Funktion empfängt, hat in vielen Fällen keinen Grund zu wissen oder sich darum zu kümmern, was der Aufrufer erwartet oder welche Ausnahmen der Aufrufer erwartet. Wenn der Code, der die Methode empfängt, nicht erwartet, dass er eine geprüfte Ausnahme auslöst, muss die bereitgestellte Methode möglicherweise alle geprüften Ausnahmen umbrechen, die sie in ungeprüften Ausnahmen auslösen würde, die ihr Lieferant dann abfangen könnte.
Supercat
0

Dies kann von Fall zu Fall variieren. In bestimmten Szenarien ist es ratsam, das zu tun, was Ihr Freund tut, z. B. wenn Sie eine API für einige Clients bereitstellen und möchten, dass der Client die Implementierungsdetails am wenigsten kennt, von denen Sie wissen, dass bestimmte Implementierungsausnahmen spezifisch sind Implementierungsdetails und nicht für den Client verfügbar.

Indem Sie die aktivierten Ausnahmen aus dem Weg räumen, können Sie APIs bereitstellen, die es dem Client ermöglichen, saubereren Code zu schreiben, da der Client selbst möglicherweise die Ausnahmebedingungen vorab überprüft.

Beispielsweise nimmt Integer.parseInt (String) einen String und gibt dessen ganzzahliges Äquivalent zurück und löst NumberFormatException aus, falls der String nicht numerisch ist. Stellen Sie sich vor, eine Formularübermittlung mit einem Feld agewird mit dieser Methode konvertiert, der Client hätte jedoch bereits die Validierung sichergestellt, sodass es keinen Grund gibt, die Prüfung auf Ausnahmen zu erzwingen.

user94369
quelle
0

Hier gibt es wirklich ein paar Fragen

  1. Sollten Sie markierte Ausnahmen in nicht markierte Ausnahmen umwandeln?

Die allgemeine Faustregel lautet, dass Ausnahmen, von denen der Anrufer erwartet, dass sie abgefangen und behoben werden, überprüft werden sollten. Andere Ausnahmen (Ausnahmen, bei denen das einzige vernünftige Ergebnis darin besteht, den gesamten Vorgang abzubrechen, oder bei denen Sie der Ansicht sind, dass es sich nicht lohnt, sich Gedanken über den Umgang mit ihnen zu machen), sollten deaktiviert werden.

Manchmal unterscheidet sich Ihre Beurteilung, ob eine Ausnahme das Abfangen und Wiederherstellen verdient, von der der API, mit der Sie arbeiten. Manchmal spielt der Kontext eine Rolle. Eine Ausnahme, die in einer bestimmten Situation eine Behandlung wert ist, ist in einer anderen möglicherweise keine Behandlung wert. Manchmal wird Ihre Hand durch vorhandene Schnittstellen gezwungen. Es gibt also berechtigte Gründe, eine aktivierte Ausnahme in eine nicht aktivierte Ausnahme (oder in einen anderen Typ einer aktivierten Ausnahme) umzuwandeln.

  1. Wenn Sie eine ungeprüfte Ausnahme in eine aktivierte Ausnahme umwandeln möchten, wie sollten Sie dies tun?

Stellen Sie zunächst und vor allem sicher, dass Sie die Funktion zum Verketten von Ausnahmen verwenden. Auf diese Weise gehen die Informationen aus der ursprünglichen Ausnahme nicht verloren und können zum Debuggen verwendet werden.

Zweitens müssen Sie entscheiden, welcher Ausnahmetyp verwendet werden soll. Die Verwendung einer einfachen Laufzeitausnahme erschwert es dem Aufrufer zu bestimmen, was schief gelaufen ist, aber wenn der Aufrufer versucht, zu bestimmen, was schief gelaufen ist, kann dies ein Hinweis darauf sein, dass Sie die Ausnahme nicht geändert haben sollten, um sie zu deaktivieren.

Peter Green
quelle
0

Bei einer Booleschen Frage ist es nach zwei kontroversen Antworten schwer, anders zu antworten, aber ich möchte Ihnen eine Perspektive geben, die selbst an wenigen Stellen erwähnt wurde, aber nicht genug betont wurde für die Bedeutung, die sie hat.

Mit den Jahren stellte ich fest, dass immer jemand über ein triviales Problem verwirrt ist, dem es an Verständnis für einige der Grundlagen mangelt.

Schichtung. Eine Software-Anwendung (sollte es zumindest sein), die aus mehreren übereinander liegenden Schichten besteht. Eine wichtige Voraussetzung für eine gute Schichtung ist, dass die unteren Schichten Funktionen für potenziell mehrere Komponenten aus der oberen Schicht bereitstellen.

Angenommen, Ihre App verfügt über die folgenden Ebenen von Grund auf: NET, TCP, HTTP, REST, DATENMODELL, BUSINESS.

Wenn Ihre Business-Schicht einen Rest-Anruf ausführen möchte ... warten Sie eine Sekunde. Warum habe ich das gesagt? Warum habe ich keine HTTP-Anfrage oder TCP-Transaktion gesendet oder Netzwerkpakete gesendet? Weil diese für meine Geschäftsschicht irrelevant sind. Ich werde nicht mit ihnen umgehen, ich werde nicht in die Details von ihnen schauen. Ich bin vollkommen in Ordnung, wenn sie tief in den Ausnahmen liegen, die ich als Ursache nehme, und ich möchte nicht wissen, dass sie überhaupt existieren.

Außerdem ist es schlecht, wenn ich Details kenne, denn wenn ich morgen die unterstrichenen Transportprotokolle ändern möchte, die sich auf Details beziehen, die für das TCP-Protokoll spezifisch sind, hat meine REST-Abstraktion keine gute Arbeit geleistet, um sich selbst zu abstrahieren die konkrete Umsetzung.

Beim Übergang einer Ausnahme von Ebene zu Ebene ist es wichtig, jeden Aspekt zu überdenken und festzustellen, welchen Sinn dies für die von der aktuellen Ebene bereitgestellte Abstraktion hat. Es kann sein, dass die Ausnahme durch eine andere ersetzt wird und mehrere Ausnahmen kombiniert werden. Sie können auch von aktiviert auf deaktiviert oder umgekehrt umgestellt werden.

Natürlich ist es eine andere Geschichte, wenn die von Ihnen genannten Orte sinnvoll sind, aber im Allgemeinen - ja, es könnte eine gute Sache sein, dies zu tun.

gsf
quelle
-2

Meiner Meinung nach,

Auf der Framework-Ebene sollten Laufzeitausnahmen abgefangen werden, um mehr Block-of-Try-Catch für den Aufrufer an derselben Stelle zu reduzieren.

In der Anwendungsebene erfassen wir selten Laufzeitausnahmen, und ich denke, diese Praxis war schlecht.

Mark xie
quelle
1
Und was machen Sie mit diesen Ausnahmen auf Framework-Ebene?
Matthieu
Wenn das Framework eine UI-Ebene enthält, die die Ausnahmen behandelt, wird auf der UI eine Fehlermeldung angezeigt, dass ein Fehler aufgetreten ist. Im Fall einer einseitigen Javascript-App kann die App eine Fehlermeldung anzeigen. Zugegeben, die UI-Ebene sollte den Fehler nur behandeln, wenn eine tiefere Ebene den Fehler nicht wirklich beheben kann.
Jake Toronto