Um mehrere mögliche Fehler zu behandeln, die die Ausführung nicht anhalten sollten, habe ich eine error
Variable, die Clients überprüfen und zum Auslösen von Ausnahmen verwenden können. Ist das ein Anti-Pattern? Gibt es eine bessere Möglichkeit, damit umzugehen? Ein Beispiel dafür finden Sie in der mysqli- API von PHP . Angenommen, Sichtbarkeitsprobleme (Accessoren, öffentlicher und privater Bereich, ist die Variable in einer Klasse oder global?) Werden korrekt behandelt.
44
try
/catch
existiert. Darüber hinaus können Sie Ihrentry
/catch
viel weiter oben im Stapel an einem geeigneten Ort ablegen, um ihn zu handhaben (was eine stärkere Trennung von Bedenken ermöglicht).Antworten:
Wenn eine Sprache von Natur aus Ausnahmen unterstützt, wird es bevorzugt, Ausnahmen auszulösen, und die Clients können die Ausnahme abfangen, wenn sie nicht möchten, dass dies zu einem Fehler führt. Tatsächlich erwarten die Clients Ihres Codes Ausnahmen und es treten viele Fehler auf, da sie die Rückgabewerte nicht überprüfen.
Die Verwendung von Ausnahmen bietet eine Reihe von Vorteilen, wenn Sie die Wahl haben.
Mitteilungen
Ausnahmen enthalten vom Benutzer lesbare Fehlermeldungen, die von den Entwicklern zum Debuggen verwendet oder den Benutzern auf Wunsch sogar angezeigt werden können. Wenn der konsumierende Code die Ausnahme nicht verarbeiten kann, kann er sie immer protokollieren, sodass die Entwickler die Protokolle durchgehen können, ohne bei jeder anderen Ablaufverfolgung anhalten zu müssen, um den Rückgabewert zu ermitteln und ihn in einer Tabelle zuzuordnen, um herauszufinden, um was es sich handelt tatsächliche Ausnahme.
Bei Rückgabewerten können auf einfache Weise keine zusätzlichen Informationen bereitgestellt werden. Einige Sprachen unterstützen das Tätigen von Methodenaufrufen, um die letzte Fehlermeldung zu erhalten, daher wird diese Sorge ein wenig gemildert. Dies erfordert jedoch, dass der Aufrufer zusätzliche Aufrufe durchführt, und manchmal den Zugriff auf ein "spezielles Objekt", das diese Informationen enthält.
Im Falle von Ausnahmemeldungen gebe ich so viel Kontext wie möglich an, wie zum Beispiel:
Vergleichen Sie dies mit einem Rückkehrcode -85. Welches würdest du bevorzugen?
Stapel aufrufen
Ausnahmen haben in der Regel auch detaillierte Aufruflisten, mit deren Hilfe Code schneller und schneller debuggt werden kann. Auf Wunsch können sie auch vom aufrufenden Code protokolliert werden. Dies ermöglicht es den Entwicklern, das Problem in der Regel auf die genaue Linie genau zu lokalisieren, und ist daher sehr leistungsfähig. Vergleichen Sie dies erneut mit einer Protokolldatei mit Rückgabewerten (z. B. -85, 101, 0 usw.). Welchen bevorzugen Sie?
Scheitern Sie schnell voreingenommen Ansatz
Wenn eine Methode an einer fehlgeschlagenen Stelle aufgerufen wird, wird eine Ausnahme ausgelöst. Der aufrufende Code muss die Ausnahme entweder explizit unterdrücken, oder sie schlägt fehl. Ich fand das wirklich erstaunlich, weil der Code während der Entwicklung und des Testens (und sogar in der Produktion) schnell ausfällt und die Entwickler gezwungen sind, ihn zu reparieren. Wenn bei Rückgabewerten die Überprüfung auf einen Rückgabewert fehlschlägt, wird der Fehler unbemerkt ignoriert und der Fehler tritt an einer unerwarteten Stelle auf, was in der Regel einen viel höheren Aufwand beim Debuggen und Beheben verursacht.
Ein- und Auspacken von Ausnahmen
Ausnahmen können in andere Ausnahmen eingeschlossen und bei Bedarf wieder entfernt werden. Beispielsweise kann Ihr Code
ArgumentNullException
den aufrufenden Code in einen Zeilenumbruch setzen,UnableToRetrievePolicyException
da dieser Vorgang im aufrufenden Code fehlgeschlagen ist. Während dem Benutzer möglicherweise eine Meldung angezeigt wird, die dem oben angegebenen Beispiel ähnelt, wird die Ausnahme möglicherweise durch einen Diagnosecode entpackt, der feststellt,ArgumentNullException
dass das Problem durch einen verursacht wurde. Dies bedeutet, dass es sich um einen Codierungsfehler im Code Ihres Verbrauchers handelt. Dies könnte dann eine Warnung auslösen, damit der Entwickler den Code korrigieren kann. Solche erweiterten Szenarien sind mit den Rückgabewerten nicht einfach zu implementieren.Einfachheit des Codes
Dieser ist etwas schwieriger zu erklären, aber ich habe durch diese Codierung sowohl mit Rückgabewerten als auch mit Ausnahmen gelernt. Der Code, der unter Verwendung von Rückgabewerten geschrieben wurde, rief normalerweise auf und überprüfte dann in einer Reihe von Schritten, wie hoch der Rückgabewert war. In einigen Fällen wird eine andere Methode aufgerufen und es werden nun weitere Überprüfungen auf die Rückgabewerte dieser Methode durchgeführt. Mit Ausnahmen ist die Ausnahmebehandlung in den meisten, wenn nicht allen Fällen viel einfacher. Sie haben einen try / catch / finally-Block, wobei die Laufzeitumgebung versucht, den Code in den finally-Blöcken zur Bereinigung auszuführen. Sogar verschachtelte try / catch / finally-Blöcke sind relativ einfacher zu verfolgen und zu verwalten als verschachtelte if / else-Blöcke und zugehörige Rückgabewerte aus mehreren Methoden.
Fazit
Wenn die von Ihnen verwendete Plattform Ausnahmen unterstützt (z. B. Java oder .NET), sollten Sie auf jeden Fall davon ausgehen, dass es keine andere Möglichkeit gibt, Ausnahmen auszulösen, da diese Plattformen Richtlinien zum Auslösen von Ausnahmen enthalten und Ihre Clients dies erwarten damit. Wenn ich Ihre Bibliothek verwenden würde, würde ich nicht die Mühe machen, die Rückgabewerte zu überprüfen, da ich davon ausgehe, dass Ausnahmen ausgelöst werden. So ist die Welt auf diesen Plattformen.
Wenn es jedoch C ++ wäre, wäre es etwas schwieriger zu bestimmen, da bereits eine große Codebasis mit Rückgabecodes vorhanden ist und eine große Anzahl von Entwicklern so eingestellt ist, dass sie Werte im Gegensatz zu Ausnahmen zurückgeben (z. B. Windows ist voll mit HRESULTs). . Darüber hinaus kann es in vielen Anwendungen auch zu Leistungsproblemen kommen (oder zumindest als solche wahrgenommen werden).
quelle
ErrorStateReturnVariable
Superklasse zu erstellen , und eine ihrer Eigenschaften istInnerErrorState
(was eine Instanz von istErrorStateReturnVariable
), welche implementierenden Unterklassen eine Kette von Fehlern anzeigen können ... oh, warten Sie. : pFehlervariablen sind ein Relikt aus Sprachen wie C, in denen Ausnahmen nicht verfügbar waren. Heute sollten Sie sie vermeiden, außer wenn Sie eine Bibliothek schreiben, die möglicherweise von einem C-Programm (oder einer ähnlichen Sprache ohne Ausnahmebehandlung) verwendet wird.
Wenn Sie einen Fehlertyp haben, der besser als "Warnung" klassifiziert werden könnte (= Ihre Bibliothek kann ein gültiges Ergebnis liefern und der Anrufer kann die Warnung ignorieren, wenn er denkt, dass dies nicht wichtig ist), dann eine Statusanzeige in Form einer Variablen kann auch in Sprachen mit Ausnahmen sinnvoll sein. Aber Vorsicht. Aufrufer der Bibliothek neigen dazu, solche Warnungen zu ignorieren, auch wenn sie dies nicht sollten. Überlegen Sie also zweimal, bevor Sie ein solches Konstrukt in Ihre Bibliothek aufnehmen.
quelle
Es gibt mehrere Möglichkeiten, einen Fehler anzuzeigen:
Das Problem der Fehlervariable besteht darin, dass das Überprüfen leicht vergessen wird.
Das Problem bei Ausnahmen besteht darin, dass verborgene Ausführungspfade erstellt werden. Obwohl try / catch einfach zu schreiben ist, ist es wirklich schwierig, eine ordnungsgemäße Wiederherstellung in der catch-Klausel sicherzustellen (keine Unterstützung von Typsystemen / Compilern).
Das Problem von Condition Handlern ist, dass sie nicht gut zusammengesetzt sind: Wenn Sie dynamischen Code ausführen (virtuelle Funktionen), ist es unmöglich vorherzusagen, welche Bedingungen behandelt werden sollen. Wenn derselbe Zustand an mehreren Stellen hervorgerufen werden kann, kann nicht gesagt werden, dass jedes Mal eine einheitliche Lösung angewendet werden kann, und es wird schnell unordentlich.
Polymorphe Renditen (
Either a b
in Haskell) sind meine bisherige Lieblingslösung:Das einzige Problem ist, dass sie möglicherweise zu übermäßiger Überprüfung führen können. Die Sprachen, in denen sie verwendet werden, haben Idiome, um die Aufrufe der Funktionen zu verketten, die sie verwenden. Möglicherweise ist jedoch noch ein bisschen mehr Tipparbeit / Unordnung erforderlich. In Haskell wären dies Monaden ; Dies ist jedoch weitaus beängstigender, als es sich anhört (siehe Eisenbahnorientierte Programmierung) .
quelle
Ich finde es schrecklich. Ich überarbeite gerade eine Java-App, die Rückgabewerte anstelle von Ausnahmen verwendet. Obwohl Sie möglicherweise überhaupt nicht mit Java arbeiten, gilt dies meines Erachtens dennoch.
Sie erhalten folgenden Code:
Oder dieses:
Ich möchte lieber, dass die Aktionen selbst Ausnahmen auslösen, sodass Sie am Ende Folgendes erhalten:
Sie können dies in einen Try-Catch einwickeln und die Meldung aus der Ausnahme abrufen oder die Ausnahme ignorieren, wenn Sie beispielsweise etwas löschen, das möglicherweise bereits nicht mehr vorhanden ist. Außerdem wird die Stapelverfolgung beibehalten, sofern Sie eine haben. Auch die Methoden selbst werden einfacher. Anstatt die Ausnahmen selbst zu behandeln, werfen sie nur das, was schief gelaufen ist.
Aktueller (schrecklicher) Code:
Neu und verbessert:
Strack-Trace bleibt erhalten und die Nachricht ist in der Ausnahme verfügbar, anstatt das nutzlose "Etwas ist schief gelaufen!".
Natürlich können Sie bessere Fehlermeldungen liefern, und das sollten Sie auch. Aber dieser Beitrag ist hier, weil der aktuelle Code, an dem ich arbeite, schmerzhaft ist und Sie nicht das Gleiche tun sollten.
quelle
throw new Exception("Something went wrong with " + instanceVar, ex);
"Um mit mehreren möglichen Fehlern fertig zu werden, sollte die Ausführung nicht unterbrochen werden."
Wenn Sie meinen, dass die Fehler die Ausführung der aktuellen Funktion nicht anhalten, sondern dem Aufrufer auf irgendeine Weise gemeldet werden sollen, dann haben Sie einige Optionen, die nicht wirklich erwähnt wurden. Dieser Fall ist eigentlich eher eine Warnung als ein Fehler. Werfen / Zurückkehren ist keine Option, da dies die aktuelle Funktion beendet. Ein einzelner Fehlermeldungsparameter oder eine einzelne Rückgabe lässt höchstens einen dieser Fehler zu.
Zwei von mir verwendete Muster sind:
Eine Fehler- / Warnungssammlung, die entweder übergeben oder als Mitgliedsvariable gespeichert wird. Dem du Sachen anhängst und einfach weiter verarbeitest. Ich persönlich mag diesen Ansatz nicht wirklich, da ich das Gefühl habe, dass er den Anrufer entmachtet.
Übergeben Sie ein Fehler- / Warnhandlerobjekt (oder legen Sie es als Elementvariable fest). Und jeder Fehler ruft eine Mitgliedsfunktion des Handlers auf. Auf diese Weise kann der Anrufer entscheiden, was mit solchen nicht beendenden Fehlern zu tun ist.
Was Sie an diese Auflistungen / Handler übergeben, sollte genügend Kontext enthalten, damit der Fehler "korrekt" behandelt werden kann. - Eine Zeichenfolge ist normalerweise zu klein. Das Übergeben einer Ausnahmebedingung ist häufig sinnvoll - wird jedoch manchmal missbilligt (als Missbrauch von Ausnahmen) .
Typischer Code, der einen Fehlerbehandler verwendet, sieht möglicherweise so aus
quelle
warnings
Paket, das ein anderes Muster für dieses Problem liefert.Oft ist nichts falsch daran, dieses oder jenes Muster zu verwenden, solange Sie das Muster verwenden, das alle anderen verwenden. In der Objective-C- Entwicklung besteht das bevorzugte Muster darin, einen Zeiger zu übergeben, mit dem die aufgerufene Methode ein NSError-Objekt ablegen kann. Ausnahmen sind Programmierfehlern vorbehalten und führen zu einem Absturz (es sei denn, Sie haben Java- oder .NET- Programmierer, die ihre erste iPhone-App schreiben). Und das funktioniert ganz gut.
quelle
Die Frage ist bereits beantwortet, aber ich kann mir nicht helfen.
Sie können nicht wirklich erwarten, dass Exception eine Lösung für alle Anwendungsfälle bietet. Jemanden hämmern?
Es gibt Fälle, in denen Ausnahmen nicht das Ende aller Ausnahmen sind. Wenn beispielsweise eine Methode eine Anforderung empfängt und für die Validierung aller übergebenen Felder verantwortlich ist, und nicht nur für die erste, müssen Sie denken, dass dies möglich sein sollte Geben Sie die Fehlerursache für mehr als ein Feld an. Es sollte auch möglich sein anzugeben, ob die Art der Validierung den Benutzer daran hindert, weiterzugehen oder nicht. Ein Beispiel dafür wäre ein nicht sicheres Passwort. Sie können dem Benutzer eine Meldung anzeigen, die besagt, dass das eingegebene Kennwort nicht sehr sicher ist, aber stark genug.
Sie könnten argumentieren, dass alle diese Überprüfungen als Ausnahme am Ende des Überprüfungsmoduls ausgegeben werden könnten, aber es handelt sich um Fehlercodes, die in nichts anderem als im Namen enthalten sind.
Die Lektion hier lautet also: Ausnahmen haben ihren Platz, ebenso wie Fehlercodes. Mit Bedacht gewählt.
quelle
Validator
(Schnittstelle) in die betreffende Methode (oder das Objekt dahinter) eingefügt sein. Abhängig von der Injektion wirdValidator
die Methode mit falschen Passwörtern fortgesetzt - oder nicht. Der umgebende Code könnte dann ein probieren,WeakValidator
wenn der Benutzer danach fragt, zB einWeakPasswordException
von dem ursprünglich ausprobierten geworfen wurdeStrongValidator
.MiddlyStrongValidator
oder etwas. Und wenn dies Ihren Ablauf nicht wirklich unterbrochen hat,Validator
muss der zuvor aufgerufen worden sein, d. H. Bevor der Ablauf fortgesetzt wird, während der Benutzer noch sein Kennwort (oder ähnliches) eingibt. Aber dann war die Validierung überhaupt nicht Teil der fraglichen Methode. :) Wahrscheinlich doch Geschmackssache ...AggregateException
(oder eine ähnlicheValidationException
) und setze für jedes Validierungsproblem in InnerExceptions bestimmte Ausnahmen. Dies kann beispielsweise seinBadPasswordException
: "Das Kennwort des Benutzers ist kürzer als die Mindestlänge von 6" oderMandatoryFieldMissingException
: "Der Vorname muss für den Benutzer angegeben werden" usw. Dies entspricht nicht den Fehlercodes. Alle diese Meldungen können dem Benutzer so angezeigt werden, dass sie verstanden werden. WennNullReferenceException
stattdessen ein geworfen wurde, ist ein Fehler aufgetreten.Es gibt Anwendungsfälle, bei denen Fehlercodes Ausnahmen vorzuziehen sind.
Wenn der Code trotz des Fehlers fortgesetzt werden kann, jedoch eine Berichterstellung erforderlich ist, ist eine Ausnahme eine schlechte Wahl, da Ausnahmen den Ablauf beenden. Wenn Sie beispielsweise eine Datendatei einlesen und feststellen, dass sie nicht terminale fehlerhafte Daten enthält, ist es möglicherweise besser, den Rest der Datei einzulesen und den Fehler zu melden, als einen vollständigen Fehler zu melden.
In den anderen Antworten wurde erläutert, warum Ausnahmen generell Fehlercodes vorzuziehen sind.
quelle
AcknowledgePossibleCorruption
Methode aufzurufen . .Es ist definitiv nichts Falsches daran, keine Ausnahmen zu verwenden, wenn Ausnahmen nicht gut passen.
Wenn die Ausführung von Code sollte nicht unterbrochen werden (zB auf Benutzereingaben handeln , die mehrere Fehler enthalten können, wie ein Programm zu kompilieren oder ein Formular - Prozess), finde ich , dass wie Fehler in Fehlervariablen zu sammeln
has_errors
underror_messages
in der Tat viel eleganter Entwurf ist als Wurf eine Ausnahme beim ersten Fehler. Es ermöglicht, alle Fehler in der Benutzereingabe zu finden, ohne den Benutzer dazu zu zwingen, diese unnötigerweise erneut zu übermitteln.quelle
In einigen dynamischen Programmiersprachen können Sie sowohl Fehlerwerte als auch die Ausnahmebehandlung verwenden . Dazu wird anstelle des normalen Rückgabewerts ein nicht ausgelöstes Ausnahmeobjekt zurückgegeben, das wie ein Fehlerwert überprüft werden kann, aber eine Ausnahme auslöst, wenn es nicht aktiviert ist.
In Perl 6 erfolgt dies über
fail
, wobei bei Verwendung vonno fatal;
scope ein spezielles nicht geworfenes Ausnahmeobjekt zurückgegebenFailure
wird.In Perl 5 können Sie Contextual :: Return verwenden, mit dem Sie dies tun können
return FAIL
.quelle
Ich halte es für eine schlechte Idee, eine Fehlervariable zur Validierung zu haben, es sei denn, es gibt etwas sehr Spezifisches. Der Zweck scheint darin zu bestehen, die für die Validierung aufgewendete Zeit zu sparen (Sie können einfach den Variablenwert zurückgeben).
Aber wenn Sie irgendetwas ändern, müssen Sie diesen Wert trotzdem neu berechnen. Ich kann nicht mehr über das Anhalten und Ausnahmewerfen sagen.
EDIT: Ich wusste nicht, dass dies eine Frage des Software-Paradigmas ist und nicht eines bestimmten Falls.
Lassen Sie mich meine Punkte in einem bestimmten Fall näher erläutern, in dem meine Antwort sinnvoll wäre
Es gibt zwei Arten von Fehlern:
In der Serviceschicht gibt es keine andere Wahl, als Result-Objekt als Wrapper zu verwenden, was der Äquivalenz der Fehlervariablen entspricht. Das Simulieren einer Ausnahme über einen Dienstaufruf über ein Protokoll wie http ist möglich, aber definitiv nicht sinnvoll. Ich spreche nicht über diese Art von Fehler und dachte nicht, dass dies die Art von Fehler ist, die in dieser Frage gestellt wird.
Ich habe über die zweite Art von Fehler nachgedacht. Und meine Antwort bezieht sich auf diese zweite Art von Fehlern. In den Entity-Objekten gibt es für uns eine Auswahl, einige von ihnen sind es
Die Verwendung der Validierungsvariablen entspricht der Verwendung einer einzelnen Validierungsmethode für jedes Entitätsobjekt. Insbesondere kann der Benutzer den Wert so einstellen, dass der Setter als reiner Setter erhalten bleibt, keine Nebenwirkung auftritt (dies ist häufig eine gute Praxis), oder er kann die Validierung in jeden Setter integrieren und das Ergebnis in einer Validierungsvariablen speichern. Dies hat den Vorteil, dass Zeit gespart wird. Das Überprüfungsergebnis wird in der Überprüfungsvariablen zwischengespeichert, sodass beim mehrmaligen Aufrufen von validation () durch den Benutzer keine mehrfache Überprüfung erforderlich ist.
In diesem Fall empfiehlt es sich, eine einzige Überprüfungsmethode zu verwenden, ohne überhaupt eine Überprüfung zum Zwischenspeichern von Überprüfungsfehlern zu verwenden. Dies hilft, den Setter als nur Setter zu halten.
quelle