Bei der vertraglichen Programmierung prüft eine Funktion oder Methode zunächst, ob ihre Voraussetzungen erfüllt sind, bevor sie mit der Bearbeitung ihrer Verantwortlichkeiten beginnt, oder? Die beiden bekanntesten Methoden für diese Überprüfungen sind nach assert
und nach exception
.
- assert schlägt nur im Debug-Modus fehl. Um sicherzustellen, dass es wichtig ist, alle separaten Vertragsvoraussetzungen (auf Einheit) zu testen, um festzustellen, ob sie tatsächlich fehlschlagen.
- Ausnahme schlägt im Debug- und Release-Modus fehl. Dies hat den Vorteil, dass das getestete Debug-Verhalten mit dem Release-Verhalten identisch ist, jedoch eine Beeinträchtigung der Laufzeitleistung zur Folge hat.
Welches ist Ihrer Meinung nach vorzuziehen?
Siehe releated Frage hier
exception
assert
design-by-contract
andreas buykx
quelle
quelle
Antworten:
Das Deaktivieren von Assert in Release-Builds entspricht der Aussage "Ich werde in einem Release-Build keinerlei Probleme haben", was häufig nicht der Fall ist. Assert sollte also in einem Release-Build nicht deaktiviert werden. Sie möchten aber auch nicht, dass der Release-Build abstürzt, wenn Fehler auftreten, oder?
Verwenden Sie also Ausnahmen und verwenden Sie sie gut. Verwenden Sie eine gute, solide Ausnahmehierarchie und stellen Sie sicher, dass Sie die Ausnahme abfangen und in Ihren Debugger einbinden, um sie abzufangen. Im Release-Modus können Sie den Fehler kompensieren und nicht einen direkten Absturz. Es ist der sicherere Weg.
quelle
Als Faustregel gilt, dass Sie Behauptungen verwenden sollten, wenn Sie versuchen, Ihre eigenen Fehler zu erfassen, und Ausnahmen, wenn Sie versuchen, die Fehler anderer Personen zu erfassen. Mit anderen Worten, Sie sollten Ausnahmen verwenden, um die Voraussetzungen für die öffentlichen API-Funktionen zu überprüfen und wann immer Sie Daten erhalten, die außerhalb Ihres Systems liegen. Sie sollten Asserts für die Funktionen oder Daten verwenden, die systemintern sind.
quelle
Das Prinzip, dem ich folge, lautet: Wenn eine Situation durch Codierung realistisch vermieden werden kann, verwenden Sie eine Behauptung. Verwenden Sie andernfalls eine Ausnahme.
Aussagen dienen dazu, sicherzustellen, dass der Vertrag eingehalten wird. Der Vertrag muss fair sein, damit der Kunde in der Lage sein muss, die Einhaltung sicherzustellen. Beispielsweise können Sie in einem Vertrag angeben, dass eine URL gültig sein muss, da die Regeln darüber, was eine gültige URL ist und was nicht, bekannt und konsistent sind.
Ausnahmen gelten für Situationen, die außerhalb der Kontrolle des Clients und des Servers liegen. Eine Ausnahme bedeutet, dass etwas schief gelaufen ist und nichts hätte getan werden können, um dies zu vermeiden. Die Netzwerkkonnektivität liegt beispielsweise außerhalb der Anwendungssteuerung, sodass nichts unternommen werden kann, um einen Netzwerkfehler zu vermeiden.
Ich möchte hinzufügen, dass die Unterscheidung zwischen Behauptung und Ausnahme nicht wirklich die beste Art ist, darüber nachzudenken. Woran Sie wirklich denken möchten, ist der Vertrag und wie er durchgesetzt werden kann. In meinem obigen URL-Beispiel ist es am besten, eine Klasse zu haben, die eine URL kapselt und entweder Null oder eine gültige URL ist. Es ist die Konvertierung einer Zeichenfolge in eine URL, die den Vertrag erzwingt, und eine Ausnahme wird ausgelöst, wenn sie ungültig ist. Eine Methode mit einem URL-Parameter ist viel klarer als eine Methode mit einem String-Parameter und einer Zusicherung, die eine URL angibt.
quelle
Behauptungen dienen dazu, etwas zu fangen, das ein Entwickler falsch gemacht hat (nicht nur Sie selbst - auch ein anderer Entwickler in Ihrem Team). Wenn es vernünftig ist, dass ein Benutzerfehler diese Bedingung verursachen könnte, sollte dies eine Ausnahme sein.
Denken Sie auch über die Konsequenzen nach. Ein Assert fährt normalerweise die App herunter. Wenn realistische Erwartungen bestehen, dass der Zustand behoben werden kann, sollten Sie wahrscheinlich eine Ausnahme verwenden.
Wenn das Problem jedoch nur auf einen Programmiererfehler zurückzuführen ist, verwenden Sie eine Bestätigung, da Sie dies so schnell wie möglich wissen möchten. Eine Ausnahme könnte abgefangen und behandelt werden, und Sie würden nie davon erfahren. Und ja, Sie sollten Asserts im Release-Code deaktivieren, da die App dort wiederhergestellt werden soll, wenn die geringste Wahrscheinlichkeit besteht. Selbst wenn der Status Ihres Programms stark beeinträchtigt ist, kann der Benutzer möglicherweise seine Arbeit speichern.
quelle
Es ist nicht genau richtig, dass "Assert nur im Debug-Modus fehlschlägt".
In Object Oriented Software Construction, 2. Auflage von Bertrand Meyer, lässt der Autor eine Tür offen, um die Voraussetzungen im Release-Modus zu überprüfen. In diesem Fall passiert, wenn eine Behauptung fehlschlägt, dass ... eine Ausnahme für eine Assertionsverletzung ausgelöst wird! In diesem Fall gibt es keine Wiederherstellung nach der Situation: Es könnte jedoch etwas Nützliches getan werden. Es besteht darin, automatisch einen Fehlerbericht zu generieren und in einigen Fällen die Anwendung neu zu starten.
Die Motivation dahinter ist, dass Voraussetzungen in der Regel billiger zu testen sind als Invarianten und Nachbedingungen, und dass in einigen Fällen Korrektheit und "Sicherheit" im Release-Build wichtiger sind als Geschwindigkeit. dh Für viele Anwendungen ist Geschwindigkeit kein Problem, sondern Robustheit (die Fähigkeit des Programms, sich sicher zu verhalten, wenn sein Verhalten nicht korrekt ist, dh wenn ein Vertrag gebrochen wird).
Sollten Sie die Vorbedingungsprüfungen immer aktiviert lassen? Es hängt davon ab, ob. Es liegt an dir. Es gibt keine universelle Antwort. Wenn Sie Software für eine Bank erstellen, ist es möglicherweise besser, die Ausführung mit einer alarmierenden Meldung zu unterbrechen, als 1.000.000 USD anstelle von 1.000 USD zu überweisen. Aber was ist, wenn Sie ein Spiel programmieren? Vielleicht brauchen Sie die Geschwindigkeit, die Sie bekommen können, und wenn jemand 1000 statt 10 Punkte wegen eines Fehlers bekommt, den die Voraussetzungen nicht erkannt haben (weil sie nicht aktiviert sind), dann haben Sie Pech.
In beiden Fällen sollten Sie diesen Fehler idealerweise während des Testens erkannt haben, und Sie sollten einen wesentlichen Teil Ihrer Tests mit aktivierten Zusicherungen durchführen. Was hier diskutiert wird, ist die beste Richtlinie für die seltenen Fälle, in denen Voraussetzungen im Produktionscode in einem Szenario fehlschlagen, das aufgrund unvollständiger Tests nicht früher erkannt wurde.
Zusammenfassend lässt sich sagen , dass Sie Zusicherungen haben und die Ausnahmen trotzdem automatisch erhalten können , wenn Sie sie aktiviert lassen - zumindest in Eiffel. Ich denke, um dasselbe in C ++ zu tun, müssen Sie es selbst eingeben.
Siehe auch: Wann sollten Zusicherungen im Produktionscode bleiben?
quelle
Es gab einen großen Thread bezüglich des Aktivierens / Deaktivierens von Behauptungen in Release-Builds auf comp.lang.c ++. Moderiert. Wenn Sie ein paar Wochen Zeit haben, können Sie sehen, wie unterschiedlich die Meinungen dazu sind. :) :)
Im Gegensatz zu coppro glaube ich, dass wenn Sie nicht sicher sind, ob eine Zusicherung in einem Release-Build deaktiviert werden kann, es keine Zusicherung sein sollte. Behauptungen sollen vor dem Bruch von Programminvarianten schützen. In einem solchen Fall gibt es für den Client Ihres Codes eines von zwei möglichen Ergebnissen:
Es gibt keinen Unterschied für den Benutzer. Es ist jedoch möglich, dass die Zusicherungen unnötige Leistungskosten für den Code verursachen, der in den allermeisten Läufen vorhanden ist, in denen der Code nicht fehlschlägt.
Die Antwort auf die Frage hängt viel mehr davon ab, wer die Clients der API sein werden. Wenn Sie eine Bibliothek schreiben, die eine API bereitstellt, benötigen Sie einen Mechanismus, um Ihre Kunden darüber zu informieren, dass sie die API falsch verwendet haben. Wenn Sie nicht zwei Versionen der Bibliothek angeben (eine mit Asserts, eine ohne), ist Assert sehr unwahrscheinlich die richtige Wahl.
Persönlich bin ich mir jedoch nicht sicher, ob ich auch in diesem Fall Ausnahmen machen würde. Ausnahmen sind besser geeignet, wenn eine geeignete Form der Wiederherstellung stattfinden kann. Beispielsweise können Sie versuchen, Speicher zuzuweisen. Wenn Sie eine 'std :: bad_alloc'-Ausnahme abfangen, können Sie möglicherweise Speicher freigeben und es erneut versuchen.
quelle
Ich habe hier meine Sicht auf den Stand der Dinge skizziert: Wie validieren Sie den internen Zustand eines Objekts? . Im Allgemeinen behaupten Sie Ihre Ansprüche und werfen Sie für die Verletzung durch andere. Zum Deaktivieren von Asserts in Release-Builds haben Sie folgende Möglichkeiten:
Natürlich sollten in Release-Builds fehlgeschlagene Zusicherungen und nicht erfasste Ausnahmen anders behandelt werden als in Debug-Builds (wo nur std :: abort aufgerufen werden kann). Schreiben Sie irgendwo ein Protokoll des Fehlers (möglicherweise in eine Datei) und teilen Sie dem Kunden mit, dass ein interner Fehler aufgetreten ist. Der Kunde kann Ihnen die Protokolldatei senden.
quelle
Sie fragen nach dem Unterschied zwischen Entwurfs- und Laufzeitfehlern.
Behauptungen sind "Hey Programmierer, das ist kaputt" -Nachrichten. Sie sollen Sie an Fehler erinnern, die Sie nicht bemerkt hätten, als sie aufgetreten sind.
Ausnahmen sind "Hey Benutzer, etwas ist schief gelaufen" -Benachrichtigungen (natürlich können Sie Code verwenden, um sie abzufangen, damit der Benutzer nie darüber informiert wird), aber diese sind so konzipiert, dass sie zur Laufzeit auftreten, wenn der Joe-Benutzer die App verwendet.
Wenn Sie also glauben, dass Sie alle Ihre Fehler beseitigen können, verwenden Sie nur Ausnahmen. Wenn Sie denken, Sie können nicht ..... verwenden Sie Ausnahmen. Sie können weiterhin Debug-Asserts verwenden, um die Anzahl der Ausnahmen natürlich zu verringern.
Vergessen Sie nicht, dass viele der Voraussetzungen vom Benutzer bereitgestellte Daten sind. Sie benötigen daher eine gute Möglichkeit, den Benutzer darüber zu informieren, dass seine Daten nicht gut waren. Dazu müssen Sie häufig Fehlerdaten im Aufrufstapel an die Bits zurückgeben, mit denen er interagiert. Behauptungen sind dann nicht nützlich - doppelt, wenn Ihre App n-Tier ist.
Schließlich würde ich keine verwenden - Fehlercodes sind für Fehler, von denen Sie glauben, dass sie regelmäßig auftreten, weit überlegen. :) :)
quelle
Ich bevorzuge den zweiten. Während Ihre Tests möglicherweise gut gelaufen sind, sagt Murphy , dass etwas Unerwartetes schief gehen wird. Anstatt beim eigentlichen fehlerhaften Methodenaufruf eine Ausnahme zu erhalten, verfolgen Sie am Ende eine NullPointerException (oder eine gleichwertige), die 10 Stapelrahmen tiefer liegt.
quelle
Die vorherigen Antworten sind richtig: Verwenden Sie Ausnahmen für öffentliche API-Funktionen. Das einzige Mal, wenn Sie diese Regel biegen möchten, ist, wenn die Prüfung rechenintensiv ist. In diesem Fall Sie können es in einer Assertion setzen.
Wenn Sie der Meinung sind, dass eine Verletzung dieser Vorbedingung wahrscheinlich ist, behalten Sie sie als Ausnahme bei oder überarbeiten Sie die Vorbedingung.
quelle
Sie sollten beide verwenden. Behauptungen dienen Ihrer Bequemlichkeit als Entwickler. Ausnahmen erfassen Dinge, die Sie zur Laufzeit verpasst oder nicht erwartet haben.
Ich habe die Fehlerberichterstattungsfunktionen von glib geliebt, anstatt nur alte Behauptungen . Sie verhalten sich wie Assert-Anweisungen, aber anstatt das Programm anzuhalten, geben sie einfach einen Wert zurück und lassen das Programm fortfahren. Es funktioniert überraschend gut und als Bonus können Sie sehen, was mit dem Rest Ihres Programms passiert, wenn eine Funktion nicht "zurückgibt, was sie soll". Wenn es abstürzt, wissen Sie, dass Ihre Fehlerprüfung irgendwo anders auf der Straße nachlässig ist.
In meinem letzten Projekt habe ich diese Art von Funktionen verwendet, um die Überprüfung von Vorbedingungen zu implementieren. Wenn eine davon fehlschlug, druckte ich einen Stack-Trace in die Protokolldatei, lief aber weiter. Ersparte mir jede Menge Debugging-Zeit, wenn andere Leute beim Ausführen meines Debug-Builds auf ein Problem stießen.
Wenn ich zur Laufzeit die Argumente überprüfen müsste, würde ich Folgendes tun:
quelle
Ich habe versucht, einige der anderen Antworten hier mit meinen eigenen Ansichten zusammenzufassen.
Verwenden Sie Assertions für Fälle, in denen Sie sie in der Produktion deaktivieren möchten, um sie zu belassen. Der einzige wirkliche Grund für die Deaktivierung in der Produktion, jedoch nicht in der Entwicklung, besteht darin, das Programm zu beschleunigen. In den meisten Fällen ist diese Beschleunigung nicht signifikant, aber manchmal ist Code zeitkritisch oder der Test ist rechenintensiv. Wenn Code geschäftskritisch ist, sind Ausnahmen trotz der Verlangsamung möglicherweise am besten.
Wenn eine echte Chance auf Wiederherstellung besteht, verwenden Sie eine Ausnahme, da Zusicherungen nicht für die Wiederherstellung ausgelegt sind. Beispielsweise wird Code selten zur Wiederherstellung nach Programmierfehlern entwickelt, sondern zur Wiederherstellung nach Faktoren wie Netzwerkfehlern oder gesperrten Dateien. Fehler sollten nicht als Ausnahmen behandelt werden, nur weil sie außerhalb der Kontrolle des Programmierers liegen. Die Vorhersagbarkeit dieser Fehler im Vergleich zu Codierungsfehlern macht sie für die Wiederherstellung freundlicher.
Zu dem Argument, dass das Debuggen von Zusicherungen einfacher ist: Der Stack-Trace einer ordnungsgemäß benannten Ausnahme ist so einfach zu lesen wie eine Zusicherung. Guter Code sollte nur bestimmte Arten von Ausnahmen abfangen, damit Ausnahmen nicht unbemerkt bleiben, weil sie abgefangen werden. Ich denke jedoch, dass Java Sie manchmal zwingt, alle Ausnahmen abzufangen.
quelle
Als Faustregel gilt für mich, dass Sie Assert-Ausdrücke verwenden, um interne Fehler und Ausnahmen für externe Fehler zu finden. Von der folgenden Diskussion von Greg können Sie hier viel profitieren .
PS: Vielleicht möchten Sie die ähnliche Frage prüfen: Ausnahme gegen Behauptung .
quelle
Siehe auch diese Frage :
quelle