Ausnahmen „Programmierfehler“ - Ist mein Ansatz korrekt?

9

Ich versuche derzeit, die Verwendung von Ausnahmen zu verbessern, und habe die wichtige Unterscheidung zwischen Ausnahmen gefunden, die Programmierfehler anzeigen (z. B. jemand hat null als Argument übergeben oder eine Methode für ein Objekt aufgerufen, nachdem es entsorgt wurde), und solchen, die einen Fehler in der Liste anzeigen Vorgang, an dem der Anrufer nicht schuld ist (z. B. eine E / A-Ausnahme).

Wie sollen diese beiden Arten von Ausnahmen unterschiedlich behandelt werden? Denken Sie, dass Fehlerausnahmen explizit dokumentiert werden müssen, oder reicht es aus, die relevanten Voraussetzungen zu dokumentieren? Und können Sie die Dokumentation einer Vorbedingung oder einer Fehlerausnahme weglassen, wenn dies offensichtlich sein sollte (z. B. ObjectDisposedExceptionbeim Aufrufen einer Methode für ein entsorgtes Objekt)?

Medo42
quelle
Normalerweise unterscheide ich eine andere Kategorie von Ausnahmen: Geschäftsausnahmen. Diese beziehen sich auf Fehlerzustände, an denen der Benutzer interessiert ist. Normalerweise mache ich diese Ausnahmen überprüft.
Beluchin

Antworten:

2

Ich denke, Sie sind auf dem richtigen Weg. Weder das Werfen, Fangen noch das Dokumentieren aller potenziell auslösbaren Ausnahmen ist sinnvoll. Es gibt Zeiten, in denen die Produktstringenz ein höheres Maß an Ausnahmebeschäftigung und Dokumentation erfordert (z. B. bestimmte sicherheitskritische Aspekte eines Systems).

Die Strategie, defensiver zu sein und Vertragskonzepte zu verwenden, um Vorbedingungen (und Nachbedingungen) für insbesondere nachgeschaltete Anrufer (z. B. alles, was einem öffentlichen oder geschützten Mitglied ähnelt) zu identifizieren, ist häufig effektiver und flexibler. Dies gilt nicht nur für die Implementierung, sondern auch für die Dokumentation. Wenn Entwickler wissen, was erwartet wird, halten sie sich eher an die Regeln und sind weniger verwirrt oder missbrauchen den von Ihnen geschriebenen Code.

Einige der häufigsten Dinge, die dokumentiert werden sollten, sind Nullparameter. Oft gibt es eine Konsequenz für ihre Verwendung, die das Ergebnis zu etwas führt, das normalerweise nicht zu erwarten ist, aber aus verschiedenen Gründen zulässig und verwendet wird, manchmal aus Gründen der Flexibilität. Als Verbraucher eines Mitglieds mit Parametern, die Nullwerte oder andere spezielle, nicht rationale Werte (wie negative Zeit oder negative Größen) zulassen, erwarte ich, dass diese identifiziert und erklärt werden.

Für Nicht-Null-Parameter möchte ich als Verbraucher eines öffentlichen oder geschützten Mitglieds wissen, dass Null nicht zulässig ist. Ich möchte wissen, wie hoch der gültige Wertebereich im angegebenen Kontext ist. Ich möchte die Konsequenzen der Verwendung von Werten kennen, die außerhalb des normalen Bereichs liegen, aber ansonsten in einem anderen aufrufenden Kontext gültig sind (z. B. ist der Wert des Typs im Allgemeinen für jede Operation gültig, jedoch nicht hier - wie bei einem booleschen Parameter, der dies nicht tut Erwarten Sie nicht false als gültigen Wert.

Was die Plattform oder andere bekannte Schnittstellen betrifft, muss man bei der Dokumentation meiner Meinung nach nicht bis zum Äußersten gehen. Da Sie als Entwickler jedoch die Möglichkeit haben, die Implementierung von den jeweiligen Plattformrichtlinien zu unterscheiden, sollten Sie beachten, wie diese Richtlinien möglicherweise von Wert sind.

Speziell für IDisposable bieten Implementierungen dieser Schnittstelle häufig eine alternative Methode, die dem expliziten Entsorgungsprozess vorgezogen wird. Markieren Sie in diesen Fällen die bevorzugte Methode und beachten Sie, dass die explizite Entsorgung nicht bevorzugt wird.

JustinC
quelle
Ich verstehe die Notwendigkeit zu dokumentieren, welche Werte zulässig sind, aber wenn Sie eine Funktion sehen performReadCommand(ICommand cmd, int replySizeBytes)- würden Sie erwarten, dass null für cmd akzeptabel ist oder ein negativer Wert für replySizeBytes? Eine IMO-Dokumentation wäre nur erforderlich, wenn diese Werte tatsächlich zulässig wären, und Sie sollten wahrscheinlich angeben, ob 0 für replySizeBytes gültig ist.
Medo42
Beide können je nach Implementierung "gültig" sein. Sie sind oder ich, und selbst jeder hier würde nicht erwarten, dass null oder negative Werte für diese Signatur geeignet sind, aber sie könnten es sein. Stellen Sie sich eine Umgebung vor, in der der Reader ein blockierender, dauerhafter Prozess ist und wenn cmd null ist, kann dies bedeuten, dass der vom Reader verwendete Befehl nicht geändert wird, und fahren Sie mit dem nächsten Lesevorgang mit dem vorherigen Befehl fort. Idealerweise wird in diesem Fall eine geeignetere Methodensignatur definiert und verwendet, bei der die cmd als Parameter nicht angegeben wurde, dies ist jedoch möglicherweise nicht der Fall.
JustinC
2

Hier sind meine eigenen Gedanken. Beachten Sie, dass ich nicht sicher bin, ob dies der beste Weg ist, weshalb ich diese Frage überhaupt erst erstellt habe.

Soweit ich weiß, ist es für einen sofortigen Aufrufer wenig sinnvoll, Programmierfehlerausnahmen tatsächlich zu behandeln. Stattdessen sollte er sicherstellen, dass die Voraussetzungen erfüllt sind. Nur "äußere" Ausnahmebehandlungsroutinen an Aufgabengrenzen sollten sie abfangen, damit sie das System am Laufen halten können, wenn eine Aufgabe fehlschlägt.

Um sicherzustellen, dass Client-Code "Fehler" -Ausnahmen sauber abfangen kann, ohne versehentlich Fehlerausnahmen abzufangen, erstelle ich jetzt meine eigenen Ausnahmeklassen für alle Fehlerausnahmen und dokumentiere sie in den Methoden, die sie auslösen. Ich würde sie in Java überprüfen Ausnahmen machen.

Bis vor kurzem habe ich versucht, alle Ausnahmen zu dokumentieren, die eine Methode auslösen könnte, aber manchmal wird eine ungewollte Liste erstellt, die in jeder Methode in der Aufrufkette dokumentiert werden muss, bis Sie zeigen können, dass der Fehler nicht auftritt. Stattdessen dokumentiere ich jetzt die Voraussetzungen in der Zusammenfassung / Parameterbeschreibung und erwähne nicht einmal, was passiert, wenn sie nicht erfüllt werden. Die Idee ist, dass die Leute sowieso nicht versuchen sollten, diese Ausnahmen explizit abzufangen, so dass es nicht notwendig ist, ihre Typen zu dokumentieren.

Für die Dokumentation der Voraussetzungen führt die Angabe des Offensichtlichen nur zu unnötiger Unordnung. Wenn die Übergabe von null an eine Methode keinen offensichtlichen Sinn ergibt, muss der Aufrufer eine Ausnahme erwarten, wenn er ohnehin null übergibt, auch wenn dies nicht dokumentiert ist. Gleiches gilt für den Fall von ObjectDisposedException - diese Schnittstelle wird so häufig verwendet, dass sich jemand, der Dispose anruft, der Verantwortung bewusst ist, sicherzustellen, dass niemand das Objekt weiterhin verwendet.

Medo42
quelle
1

Eine vernünftige Faustregel:

  1. Fangen Sie alle Ausnahmen ab, die Sie beheben können.
  2. Fangen Sie alle Ausnahmen ab, kommentieren Sie sie und werfen Sie sie erneut, die Sie nicht beheben können, über die Sie jedoch etwas Nützliches sagen können.
  3. Fangen Sie keine Ausnahme ab, die Sie nicht besser verarbeiten oder diagnostizieren können als die Standardverarbeitung.

Möglicherweise möchten Sie einen nicht schwerwiegenden Ausnahmebehandler der obersten Ebene haben, der alles abfängt, was unten nicht behandelt werden kann, um zu verhindern, dass Ihre Anwendung umfällt. Dies hängt jedoch stark von Ihrer speziellen Anwendung ab. Beispielsweise ziehen iOS-Anwendungen es vor, so viel wie möglich abzufangen. Eine Befehlszeilen-App ist möglicherweise vollkommen in Ordnung, wenn fast keine Ausnahmen erfasst werden.

Joe McMahon
quelle
0

Selbst Java "dokumentiert" nicht alle Ausnahmen. Trotz der Anforderung, dass jede thrown Ausnahme in einer throwsKlausel erwähnt werden muss, kann jede Codezeile eine RuntimeExceptionauslösen, ohne dass dies in der Methodensignatur deklariert werden muss.

Ross Patterson
quelle
Dies widerspricht jedoch den gängigen Ratschlägen - insbesondere ist Effective Java, 2. Aufl., Punkt 62 "Alle von jeder Methode ausgelösten Ausnahmen dokumentieren". Tatsächlich erkennt Bloch an, dass ungeprüfte Ausnahmen im Allgemeinen für Programmierfehler gelten, dass sie jedoch auch sorgfältig dokumentiert werden sollten, da dies "der beste Weg" ist, um die Voraussetzungen einer Methode zu dokumentieren. Ich bin mir nicht sicher, ob ich damit einverstanden bin. Wenn ich die Ausführungen dokumentiere, mache ich sie Teil des Schnittstellenvertrags und muss möglicherweise Code hinzufügen, damit sie gleich bleiben, wenn sich meine Implementierung ändert.
Medo42
1
Für jeden Experten gibt es einen Gegenexperten. Bruce Eckel, Autor des ziemlich berühmten Denkens in Java , war einmal im Lager für geprüfte Ausnahmen und hat die Seite gewechselt. Es gibt eine nette Diskussion zwischen Eckels und Anders Hejlsberg , dem Schöpfer von C #, der keine Ausnahmen überprüft hat.
Ross Patterson
0

Die Standardmethode ist der Java-Ansatz. Programmierfehler sollten nicht aktivierte Ausnahmen sein und nicht abgefangen werden, um einen schnellen Fehler zu gewährleisten. Vertragsfehler sind geprüfte Ausnahmen und sollten vom Kunden angemessen behandelt werden.

J. Ashworth
quelle
2
Betrachten Sie (z. B.) einen berechneten (oder einen Fehlerwert von) NULL als Programmierfehler oder Vertragsfehler ? Ich gehe mit Ihrer Unterscheidung, aber die Grenze ist nicht so eindeutig :)
Andrew
Wenn die Null aus einem übergebenen Argument berechnet wird (z. B. einem Null-Argument oder etwas Ungültigem), liegt ein Vertragsfehler vor. Wenn die Null aufgrund eines fehlerhaften Codes berechnet wird, ist dies ein Programmierfehler. Wenn die Null berechnet werden soll, ist dies kein Fehler. Ich sehe in diesem Beispiel keine Mehrdeutigkeit.
J. Ashworth