Ich habe eine Weile über dieses Problem nachgedacht und wäre gespannt auf Meinungen anderer Entwickler.
Ich neige zu einem sehr defensiven Programmierstil. Mein typischer Block oder meine typische Methode sieht folgendermaßen aus:
T foo(par1, par2, par3, ...)
{
// Check that all parameters are correct, return undefined (null)
// or throw exception if this is not the case.
// Compute and (possibly) return result.
}
Außerdem überprüfe ich während der Berechnung alle Zeiger, bevor ich sie dereferenziere. Meine Idee ist, dass, wenn es einen Fehler gibt und irgendwo ein NULL-Zeiger erscheinen sollte, mein Programm dies gut handhaben und sich einfach weigern sollte, die Berechnung fortzusetzen. Natürlich kann es über das Problem mit einer Fehlermeldung im Protokoll oder einem anderen Mechanismus informieren.
Um es abstrakter auszudrücken, ist mein Ansatz
if all input is OK --> compute result
else --> do not compute result, notify problem
Andere Entwickler, darunter einige meiner Kollegen, verfolgen eine andere Strategie. ZB prüfen sie keine Zeiger. Sie gehen davon aus, dass ein Teil des Codes korrekt eingegeben werden sollte und dass er nicht dafür verantwortlich sein sollte, was passiert, wenn die Eingabe falsch ist. Wenn eine NULL-Zeiger-Ausnahme das Programm zum Absturz bringt, kann ein Fehler beim Testen leichter gefunden werden und es besteht eine höhere Wahrscheinlichkeit, dass er behoben wird.
Meine Antwort darauf lautet normalerweise: Aber was ist, wenn der Fehler beim Testen nicht gefunden wird und angezeigt wird, wenn das Produkt bereits vom Kunden verwendet wird? Was ist ein bevorzugter Weg, damit sich der Fehler manifestiert? Sollte es ein Programm sein, das eine bestimmte Aktion nicht ausführt, aber trotzdem weiterarbeiten kann, oder ein Programm, das abstürzt und neu gestartet werden muss?
Zusammenfassend
Welchen der beiden Ansätze zum Umgang mit falschen Eingaben würden Sie empfehlen?
Inconsistent input --> no action + notification
oder
Inconsistent input --> undefined behaviour or crash
Bearbeiten
Danke für die Antworten und Vorschläge. Ich bin auch ein Fan von Design nach Vertrag. Aber selbst wenn ich der Person vertraue, die den Code geschrieben hat, der meine Methoden aufruft (vielleicht bin es ich selbst), kann es immer noch Fehler geben, die zu falschen Eingaben führen. Daher gehe ich davon aus, dass eine Methode niemals die richtige Eingabe erhält.
Außerdem würde ich einen Mechanismus verwenden, um das Problem abzufangen und darüber zu benachrichtigen. Auf einem Entwicklungssystem würde es zB einen Dialog öffnen, um den Benutzer zu benachrichtigen. In einem Produktionssystem würde es nur einige Informationen in das Protokoll schreiben. Ich glaube nicht, dass zusätzliche Überprüfungen zu Leistungsproblemen führen können. Ich bin mir nicht sicher, ob Behauptungen ausreichen, wenn sie in einem Produktionssystem ausgeschaltet sind: Vielleicht tritt eine Situation in der Produktion auf, die während des Testens nicht aufgetreten ist.
Wie auch immer, ich war wirklich überrascht, dass viele Leute den umgekehrten Ansatz verfolgen: Sie lassen die Anwendung "absichtlich" abstürzen, weil sie behaupten, dass dies das Auffinden von Fehlern während des Testens erleichtert.
quelle
Antworten:
Du hast es richtig gemacht. Sei paranoid. Vertrauen Sie keinem anderen Code, auch wenn es sich um Ihren eigenen Code handelt. Sie vergessen Dinge, Sie nehmen Änderungen vor, Code entwickelt sich. Vertraue keinem externen Code.
Oben wurde ein guter Punkt angesprochen: Was ist, wenn die Eingaben ungültig sind, das Programm aber nicht abstürzt? Dann bekommen Sie Müll in der Datenbank und Fehler auf der ganzen Linie.
Wenn ich nach einer Zahl gefragt werde (z. B. Preis in Dollar oder Stückzahl), gebe ich gerne "1e9" ein und sehe, was der Code bewirkt. Es kann vorkommen.
Als ich vor vier Jahrzehnten meinen Bachelor in Informatik bei UC Berkeley ablegte, wurde uns gesagt, dass ein gutes Programm zu 50% Fehlerbehandlung ist. Sei paranoid.
quelle
Sie haben bereits die richtige Idee
Welchen der beiden Ansätze zum Umgang mit falschen Eingaben würden Sie empfehlen?
oder besser
Man kann beim Programmieren nicht wirklich einen Ausstecher-Ansatz wählen (man könnte es), aber man hätte ein formelhaftes Design, das Dinge eher aus Gewohnheit als aus bewusster Wahl macht.
Temperament Dogmatismus mit Pragmatismus.
Steve McConnell sagte es am besten
Steve McConnell schrieb so ziemlich das Buch ( Code Complete ) über defensive Programmierung und dies war eine der Methoden, die er anwies, dass Sie Ihre Eingaben immer validieren sollten.
Ich kann mich nicht erinnern, dass Steve dies erwähnt hat. Sie sollten dies jedoch für nicht-private Methoden und Funktionen in Betracht ziehen , und nur für andere, wenn dies als notwendig erachtet wird.
quelle
Hier gibt es keine "richtige" Antwort, insbesondere ohne Angabe der Sprache, des Codetyps und des Produkttyps, in den der Code eingefügt werden könnte. Erwägen:
Sprache ist wichtig. In Objective-C ist es oft in Ordnung, Nachrichten an nil zu senden. es passiert nichts, aber das Programm stürzt auch nicht ab. Java hat keine expliziten Zeiger, daher sind Null-Zeiger dort kein großes Problem. In C müssen Sie etwas vorsichtiger sein.
Paranoid zu sein ist ein unvernünftiger, ungerechtfertigter Verdacht oder Misstrauen. Das ist wahrscheinlich nicht besser für Software als für Menschen.
Ihr Anliegen sollte dem Risiko im Code und der wahrscheinlichen Schwierigkeit entsprechen, auftretende Probleme zu identifizieren. Was passiert im schlimmsten Fall? Der Benutzer startet das Programm neu und fährt dort fort, wo er aufgehört hat? Das Unternehmen verliert Millionen von Dollar?
Sie können schlechte Eingaben nicht immer identifizieren. Sie können Ihre Zeiger religiös mit Null vergleichen, aber das fängt nur einen von 2 ^ 32 möglichen Werten ab, von denen fast alle schlecht sind.
Es gibt viele verschiedene Mechanismen für den Umgang mit Fehlern. Auch dies hängt bis zu einem gewissen Grad von der Sprache ab. Sie können Assert-Makros, Bedingungsanweisungen, Komponententests, Ausnahmebehandlung, sorgfältiges Design und andere Techniken verwenden. Keiner von ihnen ist narrensicher und keiner ist für jede Situation geeignet.
Es kommt also hauptsächlich darauf an, wo Sie Verantwortung übernehmen möchten. Wenn Sie eine Bibliothek für die Verwendung durch andere schreiben, möchten Sie wahrscheinlich so vorsichtig wie möglich mit den erhaltenen Eingaben sein und versuchen, hilfreiche Fehler zu melden, wenn dies möglich ist. In Ihren eigenen privaten Funktionen und Methoden könnten Sie Asserts verwenden, um dumme Fehler aufzufangen, aber ansonsten dem Anrufer (das sind Sie) die Verantwortung auferlegen, keinen Müll weiterzugeben.
quelle
Es sollte auf jeden Fall eine Benachrichtigung geben, z. B. eine ausgelöste Ausnahme. Es dient anderen Programmierern als Hinweis darauf, dass ihre Eingabe ungültig ist oder zu Fehlern führt. Dies ist sehr nützlich, um Fehler aufzuspüren. Wenn Sie einfach null zurückgeben, wird der Code so lange fortgesetzt, bis versucht wird, das Ergebnis zu verwenden und eine Ausnahme von einem anderen Code zu erhalten.
Wenn Ihr Code während eines Aufrufs eines anderen Codes (möglicherweise einer fehlgeschlagenen Datenbankaktualisierung) auf einen Fehler stößt, der über den Umfang dieses bestimmten Codeteils hinausgeht, haben Sie wirklich keine Kontrolle darüber, und Sie müssen lediglich eine Ausnahme auslösen, in der erläutert wird, was passiert Sie wissen (nur, was Ihnen durch den von Ihnen aufgerufenen Code mitgeteilt wird). Wenn Sie wissen, dass bestimmte Eingaben unweigerlich zu einem solchen Ergebnis führen, können Sie sich einfach nicht die Mühe machen, Ihren Code auszuführen und eine Ausnahme auszulösen, in der angegeben wird, welche Eingabe nicht gültig ist und warum.
In Bezug auf Endbenutzer ist es am besten, etwas Beschreibendes, aber Einfaches zurückzugeben, damit jeder es verstehen kann. Wenn Ihr Client anruft und sagt "das Programm ist abgestürzt, beheben Sie es", haben Sie viel Arbeit damit, herauszufinden, was und warum schief gelaufen ist, und hoffen, dass Sie das Problem reproduzieren können. Die ordnungsgemäße Behandlung von Ausnahmen kann nicht nur einen Absturz verhindern, sondern auch wertvolle Informationen liefern. Ein Anruf von einem Client, der sagt "Das Programm gibt mir einen Fehler. Es sagt" XYZ ist keine gültige Eingabe für Methode M, weil Z zu groß ", oder so etwas, auch wenn sie keine Ahnung haben, was es bedeutet, Sie genau wissen, wo sie suchen müssen. Abhängig von den Geschäftspraktiken Ihres Unternehmens liegt es möglicherweise auch nicht an Ihnen, diese Probleme zu beheben. Lassen Sie ihnen daher am besten eine gute Karte.
Die kurze Version meiner Antwort lautet also, dass Ihre erste Option die beste ist.
quelle
Ich hatte mit dem gleichen Problem zu kämpfen, als ich einen Universitätskurs in Programmierung absolvierte. Ich beugte mich zur paranoiden Seite und neigte dazu, alles zu überprüfen, aber mir wurde gesagt, dass dies ein irrtümliches Verhalten war.
Uns wurde "Design by contract" beigebracht. Hervorzuheben ist, dass die Voraussetzungen, Invarianten und Nachbedingungen in den Kommentaren und Konstruktionsunterlagen angegeben werden. Als Person, die meinen Teil des Codes implementiert, sollte ich dem Softwarearchitekten vertrauen und ihn befähigen, indem ich die Spezifikationen befolge, die die Voraussetzungen enthalten (welche Eingaben müssen meine Methoden verarbeiten können und welche Eingaben ich nicht senden werde). . Übermäßiges Einchecken bei jedem Methodenaufruf führt zu Aufblähungen.
Assertions sollten während Build-Iterationen verwendet werden, um die Programmkorrektheit zu überprüfen (Validierung von Vorbedingungen, Invarianten, Post-Bedingungen). Assertions würden dann in der Produktionskompilierung deaktiviert.
quelle
Die Verwendung von "Behauptungen" ist der Weg, um andere Entwickler zu benachrichtigen, dass sie es falsch machen, natürlich nur in "privaten" Methoden . Das Aktivieren / Deaktivieren ist nur ein Flag, das zum Zeitpunkt der Kompilierung hinzugefügt / entfernt werden muss. Daher ist es einfach, Zusicherungen aus dem Produktionscode zu entfernen. Es gibt auch ein großartiges Tool, mit dem Sie herausfinden können , ob Sie mit Ihren eigenen Methoden etwas falsch machen.
Was die Überprüfung von Eingabeparametern in öffentlichen / geschützten Methoden betrifft, arbeite ich lieber defensiv und überprüfe Parameter und wirfe eine InvalidArgumentException oder ähnliches. Deshalb gibt es hier für. Es hängt auch davon ab, ob Sie eine API schreiben oder nicht. Wenn es sich um eine API handelt, und noch mehr, wenn es sich um eine geschlossene Quelle handelt, sollten Sie alles besser validieren, damit die Entwickler genau wissen, was schief gelaufen ist. Andernfalls ist die Quelle, wenn sie anderen Entwicklern zur Verfügung steht, nicht schwarz / weiß. Seien Sie einfach im Einklang mit Ihren Entscheidungen.
Bearbeiten: Nur um hinzuzufügen, wenn Sie sich beispielsweise das Oracle-JDK ansehen, werden Sie feststellen, dass es niemals nach "null" sucht und den Code abstürzen lässt. Da es sowieso eine NullPointerException auslöst, sollte man sich die Mühe machen, auf Null zu prüfen und eine explizite Ausnahme auszulösen. Ich denke, es macht Sinn.
quelle