In meinem speziellen Fall kann der Benutzer einen String in die Anwendung übergeben, die Anwendung analysiert ihn und weist ihn strukturierten Objekten zu. Manchmal kann der Benutzer etwas Ungültiges eingeben. Zum Beispiel kann ihre Eingabe eine Person beschreiben, aber sie können sagen, dass ihr Alter "Apfel" ist. In diesem Fall setzen Sie die Transaktion zurück und teilen dem Benutzer mit, dass ein Fehler aufgetreten ist, und er muss es erneut versuchen. Möglicherweise muss nicht nur der erste Fehler gemeldet werden, sondern auch jeder Fehler, den wir in der Eingabe finden.
In diesem Fall habe ich argumentiert, wir sollten eine Ausnahme auslösen. Er war anderer Meinung und sagte: "Ausnahmen sollten außergewöhnlich sein: Es wird erwartet, dass der Benutzer ungültige Daten eingibt, so dass dies kein Ausnahmefall ist." scheint richtig zu sein.
Ich verstehe jedoch, dass Ausnahmen deshalb überhaupt erst erfunden wurden. Früher war es Sie hatte das Ergebnis zu überprüfen , um festzustellen , ob ein Fehler aufgetreten ist . Wenn Sie dies nicht überprüfen, können schlimme Dinge passieren, ohne dass Sie es merken.
Ohne Ausnahmen muss jede Ebene des Stapels das Ergebnis der von ihnen aufgerufenen Methoden überprüfen. Wenn ein Programmierer vergisst, eine dieser Ebenen einzuchecken, kann der Code versehentlich fortfahren und beispielsweise ungültige Daten speichern. Scheint auf diese Weise fehleranfälliger zu sein.
Wie auch immer, zögern Sie nicht, alles zu korrigieren, was ich hier gesagt habe. Meine Hauptfrage ist, ob jemand sagt, dass Ausnahmen außergewöhnlich sein sollten. Woher weiß ich, ob mein Fall außergewöhnlich ist?
quelle
Antworten:
Es wurden Ausnahmen erfunden, um die Fehlerbehandlung mit weniger Code-Unordnung zu vereinfachen. Sie sollten sie in Fällen verwenden, in denen sie die Fehlerbehandlung mit weniger Code-Unordnung erleichtern. Diese "Ausnahmen nur für außergewöhnliche Umstände" stammen aus einer Zeit, in der die Ausnahmebehandlung als inakzeptabler Leistungseinbruch eingestuft wurde. Das ist in der überwiegenden Mehrheit des Codes nicht mehr der Fall, aber die Leute sprechen die Regel immer noch aus, ohne sich an den Grund dafür zu erinnern.
Vor allem in Java, der wahrscheinlich ausnahmeliebendsten Sprache, die jemals erfunden wurde, sollten Sie sich nicht schlecht fühlen, wenn Sie Ausnahmen verwenden , um Ihren Code zu vereinfachen. Tatsächlich verfügt Javas eigene
Integer
Klasse nicht über die Möglichkeit, zu überprüfen, ob eine Zeichenfolge eine gültige Ganzzahl ist, ohne möglicherweise a auszulösenNumberFormatException
.Auch wenn Sie sich nicht nur auf die Benutzeroberflächenvalidierung verlassen können, sollten Sie bedenken, dass ein nicht numerischer Wert, der in das Back-End eingegeben wird, ein echter Wert ist, wenn Ihre Benutzeroberfläche ordnungsgemäß entworfen wurde, z außergewöhnlicher Zustand.
quelle
throw new ...
. Sie können auch benutzerdefinierte Ausnahmen auslösen, bei denen fillInStackTrace () überschrieben wird. Dann sollten Sie keine Leistungseinbußen bemerken, ganz zu schweigen von Treffern .if (foo() == ERROR) { return ERROR; } else { // continue }
auf jeder Ebene eine Kette von ausführen . Wenn Sie eine ungeprüfte Ausnahme auslösen, gibt es kein lautes und redundantes "if error return error". Wenn Sie Funktionen als Argumente übergeben, wird durch die Verwendung eines Fehlercodes möglicherweise die Funktionssignatur in einen inkompatiblen Typ geändert, obwohl der Fehler möglicherweise nicht auftritt.Wann sollte eine Ausnahme ausgelöst werden? Wenn es um Code geht, halte ich die folgende Erklärung für sehr hilfreich:
Eine Ausnahme ist, wenn ein Mitglied die Aufgabe, die es ausführen soll, wie durch seinen Namen angegeben, nicht abschließt . (Jeffry Richter, CLR über C #)
Warum ist es hilfreich? Es legt nahe, dass es vom Kontext abhängt, wann etwas als Ausnahme behandelt werden soll oder nicht. Auf der Ebene der Methodenaufrufe wird der Kontext durch (a) den Namen, (b) die Signatur der Methode und (b) den Clientcode angegeben, der die Methode verwendet oder voraussichtlich verwendet.
Um Ihre Frage zu beantworten, sollten Sie sich den Code ansehen, in dem die Benutzereingaben verarbeitet werden. Es könnte ungefähr so aussehen:
Weist der Methodenname darauf hin, dass eine Validierung durchgeführt wurde? Nein. In diesem Fall sollte eine ungültige PersonData eine Ausnahme auslösen.
Angenommen, die Klasse hat eine andere Methode, die so aussieht:
Weist der Methodenname darauf hin, dass eine Validierung durchgeführt wurde? Ja. In diesem Fall sollte eine ungültige PersonData keine Ausnahme auslösen.
Um die Dinge zusammenzufassen, schlagen beide Methoden vor, dass der Client-Code wie folgt aussehen sollte:
Wenn nicht klar ist, ob eine Methode eine Ausnahme auslösen soll, liegt möglicherweise ein schlecht gewählter Methodenname oder eine schlecht gewählte Signatur vor. Vielleicht ist das Design der Klasse nicht klar. Manchmal müssen Sie das Code-Design ändern, um eine eindeutige Antwort auf die Frage zu erhalten, ob eine Ausnahme ausgelöst werden soll oder nicht.
quelle
struct
"ValidationResult" erstellt und meinen Code so strukturiert, wie Sie ihn beschreiben.Validate
(Rückgabe von False, wenn ungültig) und einmal währendSave
(Auslösen einer bestimmten, gut dokumentierten Ausnahme, wenn ungültig). Natürlich könnte das Überprüfungsergebnis im Objekt zwischengespeichert werden, dies würde jedoch die Komplexität erhöhen, da das Überprüfungsergebnis bei Änderungen ungültig werden müsste.Validate()
es innerhalb derSave()
Methode aufgerufen wird, und bestimmte Details aus demValidationResult
könnten verwendet werden, um eine geeignete Nachricht für die Ausnahme zu erstellen.Zu diesem Argument:
Jede Ausnahme, die Sie fangen, müssen Sie erwarten, weil Sie sich entschieden haben, sie zu fangen. Nach dieser Logik sollten Sie also niemals Ausnahmen auslösen, die Sie tatsächlich abfangen möchten.
Daher denke ich, dass "Ausnahmen sollten außergewöhnlich sein" eine schreckliche Faustregel ist.
Was Sie tun sollten, hängt von der Sprache ab. Verschiedene Sprachen haben unterschiedliche Konventionen, wann Ausnahmen ausgelöst werden sollen. Python löst zum Beispiel Ausnahmen für alles aus, und wenn ich in Python bin, folge ich diesem Beispiel. C ++ hingegen wirft relativ wenige Ausnahmen, und da folge ich. Sie können C ++ oder Java wie Python behandeln und Ausnahmen für alles auslösen, aber Sie arbeiten nicht daran, wie die Sprache verwendet werden soll.
Ich bevorzuge Pythons Herangehensweise, halte es aber für eine schlechte Idee, andere Sprachen einzuschleusen.
quelle
"exceptions should be exceptional" is a terrible rule of thumb.
Gut gesagt! Das ist eines dieser Dinge, die Leute wiederholen, ohne darüber nachzudenken.Ich denke immer an Dinge wie den Zugriff auf den Datenbankserver oder eine Web-API, wenn ich an Ausnahmen denke. Sie erwarten, dass die Server- / Web-API funktioniert, in Ausnahmefällen jedoch möglicherweise nicht (der Server ist ausgefallen). Eine Webanforderung ist in der Regel schnell, in Ausnahmefällen (hohe Auslastung) tritt jedoch möglicherweise eine Zeitüberschreitung auf. Dies liegt außerhalb Ihrer Kontrolle.
Die Eingabedaten Ihrer Benutzer liegen in Ihrer Hand, da Sie überprüfen können, was diese senden und damit tun, was Sie möchten. In Ihrem Fall würde ich die Benutzereingaben validieren, bevor ich überhaupt versuche, sie zu speichern. Ich stimme eher zu, dass Benutzer, die ungültige Daten bereitstellen, erwartet werden sollten und Ihre App dies berücksichtigen sollte, indem sie die Eingabe validiert und die benutzerfreundliche Fehlermeldung ausgibt.
Trotzdem verwende ich in den meisten meiner Domain Model Setter Ausnahmen, bei denen es absolut unwahrscheinlich ist, dass ungültige Daten eingehen. Dies ist jedoch eine letzte Verteidigungslinie, und ich neige dazu, meine Eingabeformulare mit umfangreichen Validierungsregeln zu erstellen , so dass es praktisch keine Chance gibt, diese Domain-Modell-Ausnahme auszulösen. Wenn also ein Setter eine Sache erwartet und eine andere bekommt, ist dies eine Ausnahmesituation, die unter normalen Umständen nicht hätte eintreten dürfen.
EDIT (noch etwas zu beachten):
Wenn Sie vom Benutzer bereitgestellte Daten an die Datenbank senden, wissen Sie im Voraus, was Sie in Ihre Tabellen eingeben sollen und was nicht. Dies bedeutet, dass Daten anhand eines erwarteten Formats validiert werden können. Dies können Sie steuern. Was Sie nicht kontrollieren können, ist ein Serverausfall mitten in Ihrer Anfrage. Wenn Sie also wissen, dass die Abfrage in Ordnung ist und die Daten gefiltert / validiert wurden, versuchen Sie es mit der Abfrage und es schlägt immer noch fehl. Dies ist eine Ausnahmesituation.
In ähnlicher Weise können Sie bei den Webanforderungen nicht wissen, ob das Zeitlimit für die Anforderung abläuft oder die Verbindung nicht hergestellt werden kann, bevor Sie versuchen, sie zu senden. Daher ist dies auch ein Versuch / Fang-Ansatz, da Sie den Server nicht fragen können, ob er einige Millisekunden später funktioniert, wenn Sie die Anforderung senden.
quelle
FileNotFoundException
bei falscher Eingabe einen aus (z. B. einen nicht vorhandenen Dateinamen). Dies ist der einzig gültige Weg, um diesen Fehler zu gefährden. Was können Sie noch tun, ohne Fehlercodes zurückzugeben?Referenz
Vom Pragmatischen Programmierer:
Sie untersuchen das Beispiel des Öffnens einer Datei zum Lesen, und die Datei existiert nicht - sollte das eine Ausnahme auslösen?
Später diskutieren sie, warum sie diesen Ansatz gewählt haben:
In Bezug auf Ihre Situation
Ihre Frage lautet: "Sollten Validierungsfehler Ausnahmen auslösen?" Die Antwort ist, dass es davon abhängt, wo die Validierung stattfindet.
Befindet sich die betreffende Methode in einem Codeabschnitt, in dem angenommen wird, dass die Eingabedaten bereits validiert wurden, sollten ungültige Eingabedaten eine Ausnahme auslösen. Wenn der Code so konzipiert ist, dass diese Methode die exakte Eingabe eines Benutzers erhält, sind ungültige Daten zu erwarten und eine Ausnahme sollte nicht ausgelöst werden.
quelle
Hier gibt es eine Menge philosophischer Überlegungen, aber im Allgemeinen sind außergewöhnliche Bedingungen einfach solche, die Sie ohne Benutzereingriff nicht handhaben können oder wollen ( abgesehen von Bereinigung, Fehlerberichterstattung und dergleichen). Mit anderen Worten, es handelt sich um nicht behebbare Zustände.
Wenn Sie einem Programm einen Dateipfad übergeben, um diese Datei auf irgendeine Weise zu verarbeiten, und die durch diesen Pfad angegebene Datei nicht vorhanden ist, ist dies eine Ausnahmebedingung. Sie können in Ihrem Code nichts dagegen tun, als es dem Benutzer zu melden und ihm zu gestatten, einen anderen Dateipfad anzugeben.
quelle
Es gibt zwei Bedenken, die Sie berücksichtigen sollten:
Sie diskutieren ein einzelnes Anliegen - nennen wir es,
Assigner
da dieses Anliegen darin besteht, strukturierten Objekten Eingaben zuzuweisen - und Sie drücken die Einschränkung aus, dass seine Eingaben gültig sindEine gut implementierte Benutzeroberfläche hat ein zusätzliches Problem: Validierung der Benutzereingaben und konstruktives Feedback zu Fehlern (nennen wir diesen Teil
Validator
).Aus Sicht der
Assigner
Komponente ist das Auslösen einer Ausnahme absolut sinnvoll, da Sie eine Einschränkung formuliert haben, gegen die verstoßen wurde.Aus der Sicht der Benutzerfreundlichkeit kann der Benutzer sollte nicht direkt auf dieses Gespräch
Assigner
in erster Linie. Sie sollten sie reden über dieValidator
.In diesem Fall
Validator
ist eine ungültige Benutzereingabe kein Ausnahmefall, sondern ein Fall, an dem Sie mehr interessiert sind. Hier wäre eine Ausnahme also nicht angebracht, und hier möchten Sie auch alle Fehler identifizieren , anstatt Rettung bei der ersten.Sie werden bemerken, dass ich nicht erwähnt habe, wie diese Bedenken umgesetzt werden. Sie sprechen anscheinend von
Assigner
und Ihr Kollege spricht von einer KombinationValidator+Assigner
. Sobald Sie feststellen, dass es zwei getrennte (oder trennbare) Probleme gibt, können Sie dies zumindest sinnvoll diskutieren.Um auf Renans Kommentar einzugehen, gehe ich nur davon aus, dass es offensichtlich ist, welche Fälle in jedem Kontext als außergewöhnlich zu betrachten sind, wenn Sie Ihre beiden unterschiedlichen Bedenken identifiziert haben.
In der Tat, wenn es nicht offensichtlich ist, ob etwas als außergewöhnlich zu bezeichnen ist, würden Sie wahrscheinlich die unabhängigen Probleme in Ihrer Lösung noch nicht vollständig identifizieren.
Ich denke, das ist die direkte Antwort auf
weiter vereinfachen, bis es offensichtlich ist . Wenn Sie eine Menge einfacher Konzepte haben, die Sie gut verstehen, können Sie klar überlegen, ob Sie sie wieder in Code, Klassen, Bibliotheken oder was auch immer komponieren möchten.
quelle
Andere haben gut geantwortet, aber hier ist noch meine kurze Antwort. Ausnahme ist eine Situation, in der etwas in der Umgebung nicht stimmt, was Sie nicht kontrollieren können und Ihr Code überhaupt nicht vorwärts gehen kann. In diesem Fall müssen Sie den Benutzer auch darüber informieren, was schief gelaufen ist, warum Sie nicht weiter gehen können und wie die Lösung lautet.
quelle
Ich war noch nie ein großer Fan des Ratschlags, dass Sie Ausnahmen nur in Ausnahmefällen auslösen sollten, zum Teil, weil darin nichts steht (es ist wie zu sagen, dass Sie nur essbare Lebensmittel essen sollten), sondern auch, weil dies der Fall ist ist sehr subjektiv und es ist oft nicht klar, was einen Ausnahmefall darstellt und was nicht.
Es gibt jedoch gute Gründe für diesen Rat: Das Auslösen und Abfangen von Ausnahmen ist langsam, und wenn Sie Ihren Code im Debugger in Visual Studio ausführen, um Sie zu benachrichtigen, wenn eine Ausnahme ausgelöst wird, werden Sie möglicherweise von Dutzenden von Spams infiziert Wenn nicht Hunderte von Nachrichten, bevor Sie zum Problem kommen.
Also in der Regel, wenn:
dann sollte Ihr Code niemals eine Ausnahme auslösen, auch wenn diese später abgefangen wird. Um ungültige Daten abzufangen, können Sie Validatoren auf Benutzeroberflächenebene oder Code verwenden, z. B.
Int32.TryParse()
in der Präsentationsebene.Für alles andere sollten Sie sich an das Prinzip halten, dass eine Ausnahme bedeutet, dass Ihre Methode nicht das kann, was der Name sagt, was sie tut. Im Allgemeinen ist es
TryParse()
aus zwei Gründen nicht ratsam, Rückkehrcodes zu verwenden, um einen Fehler anzuzeigen (es sei denn, Ihr Methodenname gibt eindeutig an, dass dies z. B. der Fall ist ). Erstens besteht die Standardantwort auf einen Fehlercode darin, die Fehlerbedingung zu ignorieren und unabhängig davon fortzufahren. Zweitens können Sie allzu leicht mit einigen Methoden, die Rückkehrcodes verwenden, und mit anderen Methoden, die Ausnahmen verwenden, enden und vergessen, welche welche sind. Ich habe sogar Codebasen gesehen, bei denen zwei verschiedene austauschbare Implementierungen derselben Schnittstelle hier unterschiedliche Ansätze verfolgen.quelle
Ausnahmen sollten Bedingungen darstellen , die es wahrscheinlich die sofortige Berufung Code nicht zu handhaben , auch wenn die anrufende Methode könnte hergestellt werden. Angenommen, Code, der einige Daten aus einer Datei liest, geht zu Recht davon aus, dass eine gültige Datei mit einem gültigen Datensatz endet und keine Informationen aus einem Teildatensatz extrahieren muss.
Wenn die Routine read-data keine Ausnahmen verwendet, sondern lediglich meldet, ob der Lesevorgang erfolgreich war oder nicht, müsste der aufrufende Code folgendermaßen aussehen:
usw. für jede nützliche Arbeit drei Codezeilen ausgeben. Wenn dagegen
readInteger
will beim Erreichen des Endes einer Datei eine Ausnahme auslöst und der Aufrufer die Ausnahme einfach weiterleiten kann, lautet der Code wie folgt:Viel einfacher und sauberer aussehend, mit weitaus größerem Schwerpunkt auf dem Fall, dass die Dinge normal funktionieren. Beachten Sie, dass in Fällen, in denen der unmittelbare Aufrufer die Behandlung einer Bedingung erwartet, eine Methode, die einen Fehlercode zurückgibt, oft hilfreicher ist als eine, die eine Ausnahme auslöst. So summieren Sie beispielsweise alle Ganzzahlen in einer Datei:
gegen
Der Code, der nach den ganzen Zahlen fragt, erwartet, dass einer dieser Aufrufe fehlschlägt. Wenn der Code eine Endlosschleife verwendet, die bis dahin ausgeführt wird, ist dies weitaus weniger elegant als die Verwendung einer Methode, die Fehler über ihren Rückgabewert anzeigt.
Da Klassen häufig nicht wissen, welche Bedingungen ihre Clients erwarten oder nicht erwarten, ist es oft hilfreich, zwei Versionen von Methoden anzubieten, die auf eine Weise fehlschlagen können, die einige Anrufer erwarten und andere nicht. Auf diese Weise können solche Methoden bei beiden Anrufertypen problemlos verwendet werden. Beachten Sie auch, dass selbst "try" -Methoden Ausnahmen auslösen sollten, wenn Situationen auftreten, die der Aufrufer wahrscheinlich nicht erwartet. Zum Beispiel
tryReadInteger
sollte keine Ausnahme ausgelöst werden, wenn eine saubere End-of-File-Bedingung auftritt (wenn der Anrufer dies nicht erwartet hätte, hätte der Anrufer verwendetreadInteger
). Andererseits sollte es wahrscheinlich eine Ausnahme auslösen, wenn die Daten nicht gelesen werden konnten, weil z. B. der Memory Stick, auf dem sie gespeichert waren, nicht eingesteckt war. Während solche Ereignisse immer als Möglichkeit erkannt werden sollten, ist es unwahrscheinlich, dass der Code für den sofortigen Aufruf bereit ist, als Antwort etwas Nützliches zu tun. Es sollte auf keinen Fall auf die gleiche Weise gemeldet werden, wie dies bei einer Dateiende-Bedingung der Fall wäre.quelle
Das Wichtigste beim Schreiben von Software ist die Lesbarkeit. Alle anderen Überlegungen sind zweitrangig, einschließlich ihrer Effizienz und Richtigkeit. Wenn es lesbar ist, kann der Rest bei der Wartung erledigt werden. Wenn es nicht lesbar ist, ist es besser, es einfach wegzuwerfen. Aus diesem Grund sollten Sie Ausnahmen auslösen, wenn die Lesbarkeit verbessert wird.
Denken Sie beim Schreiben eines Algorithmus nur an die Person in der Zukunft, die ihn lesen wird. Wenn Sie an einen Ort kommen, an dem ein potenzielles Problem auftreten könnte, fragen Sie sich, ob der Leser sehen möchte, wie Sie mit diesem Problem jetzt umgehen , oder ob der Leser es vorziehen würde, einfach mit dem Algorithmus fortzufahren.
Ich denke gerne an ein Rezept für Schokoladenkuchen. Wenn Sie aufgefordert werden, die Eier hinzuzufügen, haben Sie die Wahl: Entweder nehmen Sie Eier an und fahren mit dem Rezept fort, oder es wird erläutert, wie Sie Eier erhalten, wenn Sie keine Eier haben. Es könnte ein ganzes Buch mit Techniken für die Jagd auf wilde Hühner füllen, um Ihnen beim Backen eines Kuchens zu helfen. Das ist gut, aber die meisten Leute werden dieses Rezept nicht lesen wollen. Die meisten Leute würden es vorziehen, einfach anzunehmen, dass Eier verfügbar sind, und mit dem Rezept weitermachen. Das ist ein Urteil, das die Autoren beim Schreiben von Rezepten treffen müssen.
Es gibt keine garantierten Regeln darüber, was eine gute Ausnahme ausmacht und welche Probleme sofort behoben werden sollten, da Sie dazu die Gedanken Ihres Lesers lesen müssen. Das Beste, was Sie jemals tun werden, sind Faustregeln, und "Ausnahmen gelten nur für außergewöhnliche Umstände" ist eine ziemlich gute. Wenn ein Leser Ihre Methode liest, sucht er in der Regel in 99% der Fälle nach den Möglichkeiten, die die Methode bietet, und es ist ihnen lieber, wenn keine bizarren Eckfälle vorliegen, z. Sie möchten den normalen Ablauf Ihrer Software direkt sehen, eine Anweisung nach der anderen, als ob niemals Probleme auftreten würden.
quelle
Aus diesem Grund können Sie hier keine Ausnahme auslösen. Eine Ausnahme unterbricht den Validierungsprozess sofort. Es würde also viel Arbeit geben, um dies zu erreichen.
Ein schlechtes Beispiel:
Validierungsmethode für
Dog
Klassen mit Ausnahmen:Wie man es nennt:
Das Problem hierbei ist, dass der Überprüfungsprozess, um alle Fehler zu erhalten, das Überspringen bereits gefundener Ausnahmen erfordern würde. Das oben Genannte könnte funktionieren, aber dies ist ein klarer Missbrauch von Ausnahmen . Die Art der Überprüfung, nach der Sie gefragt wurden, sollte erfolgen, bevor die Datenbank berührt wird. Sie müssen also nichts zurücksetzen. Und die Ergebnisse der Validierung sind wahrscheinlich Validierungsfehler (hoffentlich Null).
Der bessere Ansatz ist:
Methodenaufruf:
Validierungsmethode:
Warum? Es gibt unzählige Gründe, und die meisten wurden in den anderen Antworten angesprochen. Einfach ausgedrückt: Es ist viel einfacher, für andere zu lesen und zu verstehen. Zweitens möchten Sie dem Benutzer Stack-Traces anzeigen, um ihm zu erklären, dass er seine
dog
falsch eingerichtet hat?Wenn während des Festschreibens im zweiten Beispiel immer noch ein Fehler auftritt , obwohl Ihr Prüfer die
dog
Probleme mit null validiert hat, ist es richtig, eine Ausnahme auszulösen . Wie: Keine Datenbankverbindung, der Datenbankeintrag wurde in der Zwischenzeit von einer anderen Person geändert, oder so ähnlich.quelle