Frage

Unchecked exceptions sind in Ordnung, wenn Sie möchten, behandeln Sie jedes scheitern die gleiche Weise, zum Beispiel durch anmelden und überspringen, um die nächste Anfrage mit einer Nachricht an den Benutzer und den Umgang mit dem nächsten event, etc.Wenn dies meine, alles, was ich zu tun ist, fangen einige Allgemeine Ausnahme-Typ auf einem hohen Niveau in meinem system, und behandeln Sie alles auf die gleiche Weise.

Aber ich wiederherstellen möchten, die von bestimmten Problemen, und ich bin sicher nicht der beste Weg, es mit ungeprüften Ausnahmen.Hier ist ein konkretes Beispiel.

Angenommen, ich habe eine web-Anwendung gebaut, mit Struts2 und Hibernate.Wenn eine Ausnahme Blasen bis zu meiner "Aktion", ich log Sie, und zeigen Sie eine ziemlich Entschuldigung an den Benutzer.Aber eine der Funktionen, die von meiner web-Anwendung ist die Erstellung von neuen Benutzerkonten, die erfordern eine eindeutige Benutzer-Namen.Wenn ein Benutzer wählt einen Namen, der bereits vorhanden ist, Hibernate wirft ein org.hibernate.exception.ConstraintViolationException (eine ungeprüfte Ausnahme) nach unten in den Eingeweiden von meinem system.Ich möchte wirklich, um zu erholen von diesem speziellen problem, indem er die Benutzer zu wählen Sie einen anderen Benutzernamen, anstatt Ihnen den gleichen "wir protokolliert, die Ihr problem, aber für jetzt bist du abgespritzt." angezeigt wird.

Hier sind ein paar Punkte zu berücksichtigen:

  1. Es gibt eine Menge Leute, die das erstellen von Konten gleichzeitig.Ich will nicht zum sperren der gesamten Tabelle user zwischen einem "SELECT", um zu sehen, ob der name vorhanden ist und ein "EINFÜGEN", wenn nicht.Im Fall von relationalen Datenbanken, möglicherweise gibt es einige tricks, um dies zu umgehen, aber was mich wirklich interessiert, ist der Allgemeine Fall, wo die vor-Prüfung für eine Ausnahme nicht Arbeit wegen der grundsätzlichen race-Bedingung.Dasselbe könnte von der Suche nach einer Datei auf dem Dateisystem, etc.
  2. Mein CTO Neigung für drive-by-management, die durch das Lesen technology Spalten in "Inc.", Ich brauche eine Dereferenzierung rund um die Persistenz-Mechanismus, so dass ich kann werfen den Ruhezustand und die Nutzung Kodo, oder was auch immer, ohne etwas zu ändern, außer die Unterste Schicht von Ausdauer-code.Als eine Angelegenheit von der Tat, es gibt mehrere Ebenen der Abstraktion in meinem system.Wie kann ich verhindern, dass Sie undicht trotz unchecked exceptions?
  3. Einer der deklamierten Schwächen überprüft Ausnahmen ist, dass man "behandeln" Sie wird bei jedem Aufruf auf den stack—entweder, indem er erklärte, daß eine Berufung auf die Methode wirft, oder fangen Sie Sie und behandeln.Sie zu behandeln bedeutet oft, wickelte Sie in eine andere überprüft Ausnahme von einem Typ entsprechend der Ebene der Abstraktion.So, zum Beispiel, im aufgegebenen Ausnahme land, ein file-system–basierte Implementierung von meinem benutzerregistrys umgesetzt fangen könnte IOException, während ein Datenbank-Implementierung würde fangen SQLException, aber beide würden werfen UserNotFoundException das blendet die zugrunde liegende Implementierung.Wie kann ich die Vorteile von ungeprüften Ausnahmen, ohne mich von der Last dieser Verpackung auf jeder Ebene, ohne zu lecken Implementierungsdetails?
War es hilfreich?

Lösung

IMO, Verpackung Ausnahmen (checked oder anderweitig) hat mehrere Vorteile, die es Wert sind die Kosten:

1) Es ermutigt Sie zu denken, über die ausfallarten für den code, den Sie schreiben.Grundsätzlich zu beachten, dass die Ausnahmen, die dem code, den Sie anrufen, kann werfen, und im Gegenzug werden Sie betrachten die Ausnahmen werfen Sie für den code, Anrufe verkaufen.

2) Es gibt Ihnen die Möglichkeit, zusätzliche debugging-Informationen in die exception-Kette.Zum Beispiel, wenn Sie haben eine Methode, die eine exception wirft eine doppelte Benutzernamen, Sie könnte wrap mit Ausnahme einer, die enthält zusätzliche Informationen über die Umstände des Fehlers (zum Beispiel die IP der Anfrage, die als dupe Benutzername), war nicht verfügbar, um die Low-level-code.Die cookie-trail Ausnahmen können helfen Sie Debuggen ein Komplexes problem (es hat sicherlich für mich).

3) Es ermöglicht Ihnen, sich von der Implementierung unabhängig von der unteren Ebene code.Wenn Sie die Verpackung Ausnahmen und müssen swap out Hibernate für einige andere ORM, Sie müssen nur ändern Ihre Hibernate-handling-code.Alle anderen Ebenen der code wird immer noch erfolgreich mit dem eingebundenen Ausnahmen und interpretieren Sie auf die gleiche Weise, obwohl die zugrunde liegenden Umstände geändert haben.Beachten Sie, dass dies gilt, selbst wenn Ruhezustand ändert sich in irgendeiner Weise (ex:Sie wechseln Ausnahmen in einer neuen version);es ist nicht nur für Großhandel Technologie Austausch.

4) Es fordert Sie verwenden verschiedene Klassen von Ausnahmen für die Darstellung verschiedener Situationen.Für Beispiel, Sie können eine DuplicateUsernameException, wenn der Benutzer versucht, verwenden Sie einen Benutzernamen und ein DatabaseFailureException, wenn Sie können nicht überprüfen dupe Benutzernamen aufgrund eines Defekten DB-Verbindung.Dies wiederum ermöglicht Ihnen die Antwort auf Ihre Frage ("wie kann ich Sie wiederherstellen?") flexible und leistungsfähige Möglichkeiten.Wenn Sie eine DuplicateUsernameException, können Sie sich entscheiden, schlagen Sie einen anderen Benutzernamen für den Benutzer.Wenn Sie eine DatabaseFailureException, Sie können lassen Sie es Blase bis zu dem Punkt wo es wird ein "nach unten für die Wartung" - Seite, die Benutzer und senden Sie eine Benachrichtigung E-Mail zu.Sobald Sie benutzerdefinierte Ausnahmen haben Sie individuelle Antworten-und das ist eine gute Sache.

Andere Tipps

Ich mag zu Verpacken Ausnahmen zwischen den "Stufen" meiner Anwendung, so zum Beispiel eine DB-spezifische Ausnahme ist, neu verpackt innerhalb eine weitere Ausnahme ist die Aussage in den Kontext meiner Anwendung (natürlich lasse ich die ursprüngliche Ausnahme als Mitglied, damit ich nicht den Kram, den stack-trace).

Sagte, ich denke, dass eine nicht-eindeutige Benutzer-name ist nicht "außergewöhnlich" genug, die situation zu garantieren werfen.Ich würde verwenden Sie einen booleschen argument statt.Ohne zu wissen viel über Ihre Architektur, es ist schwer für mich zu sagen, nichts mehr spezifische oder anwendbar.

Finden Muster für die Erzeugung, Handhabung und Management von Fehlern

Von der Split-Domain-und Technische Fehler pattern

Ein technischer Fehler soll nie die Ursache domain-Fehler generiert werden (nie der twain treffen sollte).Wenn ein technische Fehler verursachen business Verarbeitung zu scheitern, sollte es sein eingewickelt wie ein SystemError.

Domain-Fehler sollten immer aus einer domain problem und verarbeitet werden - domain-code.

Domain-Fehler pass - "nahtlos" durch die technische Grenzen.Es kann sein, dass solche Fehler die serialisiert werden muss und re-konstituiert um dies zu verwirklichen.Proxies und Fassaden übernehmen die Verantwortung für dies zu tun.

Technische Fehler sollten behandelt werden insbesondere die Punkte in der Anwendung, wie Grenzen (siehe Log in Distribution Boundary).

Die Menge von Kontext-Informationen übergeben zurück mit der Fehlermeldung hängt davon ab, wie nützlich ist dies für nachfolgende Diagnose und Behandlung (, herauszufinden, eine alternative Strategie).Sie müssen die Frage, ob der stack-trace aus ein remote-Rechner ist ganz nützlich die Verarbeitung des domain-Fehler (obwohl der code Lage des Fehler und die Werte der Variablen an, die Zeit kann nützlich sein)

So wickeln Sie den Ruhezustand Ausnahme an der Grenze zu überwintern mit einer unkontrollierten domain Ausnahme, wie ein "UniqueUsernameException", und lassen, dass die Blase bis alle den Weg zum Aufbereiter es.Stellen Sie sicher, dass javadoc der ausgelösten Ausnahme, obwohl es ist nicht eine geprüfte Ausnahme!

Da Sie gerade mit hibernate die einfachste Sache zu tun ist, ist check für die Ausnahme und wickeln Sie es in eine benutzerdefinierte Ausnahme-oder in ein benutzerdefiniertes Ergebnis-Objekt, das Sie haben können setup in Ihrem Rahmen.Wenn Sie wollen zu Graben hibernate später nur sicher, dass Sie wickeln diese Ausnahme in nur 1 Ort, der erste Ort, den Sie fangen Sie die Ausnahme aus dem Ruhezustand, das ist der code, den Sie wahrscheinlich ändern müssen, wenn Sie einen Schalter sowieso, also, wenn der Haken ist an einem Ort dann der zusätzliche Aufwand ist fast zilch.

Hilfe?

Ich Stimme mit Nick.Ausnahme, die Sie beschrieben haben, nicht wirklich "unerwartete Ausnahme", so sollten Sie die design Sie code entsprechend eine mögliche Ausnahmen zu berücksichtigen.

Auch würde ich empfehlen, einen Blick auf die Dokumentation von Microsoft Enterprise Library Ausnahmebehandlung es hat eine schöne Gliederung der Fehlerbehandlung Muster.

Sie fangen können ungeprüfte Ausnahmen zu, ohne sich zu wickeln.Für Beispiel, die folgenden ist gültig Java.

try {
    throw new IllegalArgumentException();
} catch (Exception e) {
    System.out.println("boom");
}

Also in Ihrem handeln/ - controller können Sie einen try-catch-block, um die Logik, wo die Hibernate-Aufruf erfolgt.Je nach der Ausnahme, die Sie machen können spezifische Fehlermeldungen.

Aber ich denke, in Ihrem heute könnte es sein, Hibernate, und morgen SleepLongerDuringWinter framework.In diesem Fall müssen Sie so tun, als hätte Sie Ihren eigenen kleinen ORM-framework, dass wraps um den Dritten Rahmen.Dies wird Ihnen ermöglichen, wickeln Sie jede framework-spezifische Ausnahmen in sinnvoller und/oder geprüft Ausnahmen, dass Sie wissen, wie man besser verstehen kann.

  1. Die Frage ist nicht wirklich im Zusammenhang mit überprüft vs.ungeprüfter Debatte, das gleiche gilt für die beiden Ausnahme-Typen.

  2. Zwischen dem Punkt, wo die ConstraintViolationException geworfen und den Punkt, wo wir wollen, behandeln Sie die Verletzung durch die Anzeige eine nette Fehlermeldung ist eine große Anzahl von Methodenaufrufen auf dem Stapel, sollte sofort Abbrechen und sollte sich keine Gedanken über das problem.Das macht der Ausnahme-Mechanismus die richtige Wahl im Gegensatz zu einer Neugestaltung der code von Ausnahmen, die Werte zurückgeben.

  3. In der Tat, mit einer ungeprüften Ausnahme anstatt eine checked exception ist eine Natürliche Ergänzung, da wir wirklich wollen, alle zwischen Methoden, die auf dem Aufruf-stack, um ignorieren die Ausnahme und die nicht Griff es .

  4. Wenn wir wollen, zu behandeln die "eindeutigen Namen Verletzung" nur durch die Anzeige eine nette Fehlermeldung (Fehler-Seite) zu die Benutzer, es gibt nicht wirklich eine Notwendigkeit für eine bestimmte DuplicateUsernameException.Diese wird halten die Anzahl von exception-Klassen gering ist.Stattdessen können wir eine MessageException kann wiederverwendet werden in viele ähnliche Szenarien.

    So bald wie möglich, wir fangen die ConstraintViolationException und wandeln es in ein MessageException mit einer netten Nachricht.Es ist wichtig zu konvertieren es bald, wenn wir sicher sein können, dass es wirklich der unique user name Einschränkung", die verletzt wurde und nicht eine andere Einschränkung.

    Irgendwo in der Nähe der top-level-handler, nur Griff die MessageException in einer anderen Art und Weise.Statt "wir protokolliert, die Ihr problem, aber für jetzt bist du abgespritzt" zeigen einfach nur die Nachricht enthielt in der MessageException, keine stack-trace.

    Die MessageException kann einige zusätzliche Konstruktor-Parameter, wie eine ausführliche Erläuterung des Problems, im nächsten Aktion (Abbrechen, auf eine andere Seite), Symbol (Fehler, Warnung)...

Der code könnte wie folgt Aussehen

// insert the user
try {
   hibernateSession.save(user);
} catch (ConstraintViolationException e) {
   throw new MessageException("Username " + user.getName() + " already exists. Please choose a different name.");
}

In einer völlig anderen Ort, es ist ein top-exception-handler

try {
   ... render the page
} catch (MessageException e) {
   ... render a nice page with the message
} catch (Exception e) {
   ... render "we logged your problem but for now you're hosed" message
}

@Jan Checked vs. unchecked ist eine zentrale Frage hier.Ich Frage die Annahme, (#3), dass die Ausnahme ignoriert werden sollen, in den dazwischen liegenden frames.Wenn ich das mache, werde ich am Ende mit einer Umsetzung-spezifische Abhängigkeit in meine high-level-code.Wenn ich ersetzen, Hibernate, catch-Blöcke in meiner gesamten Anwendung geändert werden.Doch zur gleichen Zeit, wenn ich fangen Sie die Ausnahme auf einer niedrigeren Ebene, ich bin nicht erhalten viel nutzen aus der Verwendung einer ungeprüften Ausnahme.

Auch das Szenario hier ist, dass ich möchte, zu fangen, eine bestimmte logische Fehler, und ändern die Fluss der Anwendung von re -, dass der Benutzer eine andere ID.Einfaches ändern der angezeigten Meldung ist nicht gut genug, und die Möglichkeit der Zuordnung zu verschiedenen Nachrichten basierend auf dem Typ der Ausnahme ist gebaut in Servlets schon.

@erikson

Nur um Lebensmittel hinzufügen, um Ihre Gedanken zu:

Checked vs. unchecked ist auch diskutiert hier

Die Verwendung von ungeprüften Ausnahmen ist konform mit der Tatsache, dass Sie verwendet werden IMO für die Ausnahme verursacht durch den Aufrufer der Funktion zurückgegeben (und der Anrufer kann mehrere Schichten oben, die Funktion, daher die Notwendigkeit für die anderen frames zu ignorieren, die Ausnahme)

In Bezug auf Ihre spezifische Frage, Sie fangen sollte die ungeprüfte Ausnahme auf hohem Niveau und Kapseln Sie, wie gesagt von @Kanook in Ihre eigene Ausnahme, ohne dass der Aufrufstapel (als wissen von @Jan Soltis )

That being said, wenn die zugrunde liegenden Technologie ändert, wird dies Einfluss haben auf jene catch() bereits in Ihrem code, und das ist nicht die Antwort auf Ihre Letzte Szenario.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top