it-swarm-eu.dev

Sollte eine Methode ihre Parameter validieren?

Angenommen, Sie entwerfen eine Quadratwurzelmethode sqrt. Möchten Sie lieber überprüfen, ob der übergebene Parameter keine negative Zahl ist, oder überlassen Sie es dem Aufrufer, sicherzustellen, dass der übergebene Parameter gültig ist? Wie variiert Ihre Antwort, wenn die Methode/API für den Verbrauch durch Dritte bestimmt ist oder wenn sie nur für die bestimmte Anwendung verwendet wird, an der Sie arbeiten

Ich war der Meinung, dass eine Methode ihren Parameter validieren sollte. Pragmatic Programmer sagt jedoch in seinem Abschnitt Design by Contract (Kapitel 4), dass es in der Verantwortung des Aufrufers liegt, gute Daten zu übergeben (S. 111 und 115), und schlägt vor, Assertions in der Methode zu verwenden um das gleiche zu überprüfen. Ich möchte wissen, was andere darüber denken.

44
Amit Wadhwa

Im Allgemeinen entwerfe ich meine APIs wie folgt:
1. Dokumentieren Sie die Methoden gut und ermutigen Sie die Anrufer, gute/gültige Daten weiterzugeben.
2. Überprüfen Sie die Parameter trotzdem! - Ausnahmen auslösen, wenn die Voraussetzungen nicht erfüllt sind.

Ich würde sagen, dass die Parameterüberprüfung bei den meisten öffentlich zugänglichen APIs erforderlich ist. Die Parametervalidierung bei nicht öffentlichen Methoden ist nicht so wichtig - es ist oft wünschenswert, dass die Validierung nur einmal am öffentlichen „Einstiegspunkt“ erfolgt -, aber wenn Sie mit dem potenziellen Leistungseinbruch leben können, validiere ich Parameter gerne überall Dies erleichtert die Code-Wartung und das Refactoring ein wenig.

34
Matthew King

Wenn Sie immer Parameter validieren, erledigen Sie zusätzliche Arbeiten, die möglicherweise nicht benötigt werden.

Stellen Sie sich die Situation vor, in der die Eingabe bereits vor dem Anruf validiert wurde und Sie jetzt die Daten im Anruf erneut validieren. OK, eine zusätzliche Validierungsprüfung ist OK, aber jetzt erweitern Sie diese Logik auf alle Funktionen in Ihrer Anwendung. Jeder Funktionsaufruf validiert die Daten, obwohl sie zuvor validiert wurden (jetzt mehrmals).

Die Daten sollten an EINEM Punkt validiert werden. Hier werden die Daten in das Programm (oder das Subsystem) eingegeben (dies gibt einen gewissen Spielraum, da die Definition des Subsystems flexibel sein kann). Wenn keine Programmierfehler vorliegen, sollte Ihr Code jetzt funktionieren (Hinweise zur Überprüfung von fehlerhaftem Code unterscheiden sich von der Überprüfung von Parametern).

Wenn Sie Parameter wirklich validieren möchten, haben Sie zwei Versionen der Funktion. Eine zu validieren und eine, die nicht validiert. Schauen Sie auf std :: vector (Operator [] validiert nicht, während at () validiert).

Wenn ich also eine sqrt () -Funktion entwerfen müsste, würde sie nicht ihre Eingaben validieren, da in den meisten Situationen die Daten sowieso gut wären und die kleine Minderheit der Situationen, in denen sie möglicherweise falsch sind, der Benutzer kann Führen Sie eine schnelle Überprüfung durch (wie bei Benutzereingaben kann dies falsch sein und muss vor der Verwendung überprüft werden). Das einzige andere Mal, wenn es falsch ist, ist ein Programmiererfehler (und Ihre Unit-Tests sollten diese abfangen).

26
Martin York

Wie viel Redundanz/Robustheit sollte komplexe Software implementieren? stellt eine verwandte Frage.

Meine Antwort lautet, dass Funktionen, die mit der Außenwelt interagieren (öffentliche APIs, UI-Methoden, Dateireader usw.), Eingaben validieren und den Benutzer so höflich und klar wie möglich über Fehler informieren sollten. Zumindest sollte die Fehlermeldung/der Rückkehrcode angeben, wo die fehlerhafte Eingabe aufgetreten ist und gegen welche Art von Einschränkung sie verstoßen hat. Je spezifischer desto besser.

Auf der anderen Seite sollten private Funktionen, die sich nur mit intern generierten Daten oder Daten befassen, die bereits von einer der nach außen gerichteten Funktionen verarbeitet wurden, Aussagen über eine der Voraussetzungen haben, die für einen erfolgreichen Betrieb erforderlich sind. Wenn ein Programmierer Code schreibt, der gegen diese Regeln verstößt, sollte das Programm immer wieder fehlschlagen. Diese Art von Fehler sollte es niemals über die frühen Testphasen hinaus schaffen.

Die Motivation dabei ist, den Benutzern so nett wie möglich zu sein und gleichzeitig die Entscheidungen über den Umgang mit schlechten Eingaben auf ein möglichst hohes Niveau zu beschränken. Sie möchten nicht jedes Mal, wenn Sie eine Funktion auf niedriger Ebene schreiben, den nächsten Teil Ihrer Fehlerbehandlungsstrategie auf Programmebene herausfinden.

Also: Benutzereingaben validieren, Programmierereingaben bestätigen.

17
AShelly

Wenn Sie ein Typsystem haben, verwenden Sie dieses.

Behauptungen werden helfen, das Böse frühzeitig zu erkennen.

nicht-Null-Einschränkungen sind eine vernünftige Sache.

sqrt (-1) ist in einigen Programmiersprachen kein Fehler. Smalltalks mit komplexer Nummernunterstützung geben nur i zurück.

10
Tim Williscroft

Ich denke, es hängt eher von der Anwendung der Methode als von der Implementierungstheorie ab.

Was ich meine ist: Wenn Sie eine schnelle Mathematikbibliothek erstellen, obwohl sie von irgendjemandem verwendet werden kann, möchten Sie keine Laufzeitprüfungen durchführen, zumindest nicht, wenn sie im Release-Modus erstellt wurden, da Geschwindigkeit die Beurteilung ist Kriterium. Sie können Überprüfungen in einem Debug-Modus mithilfe von Zusicherungen implementieren, da das Verhalten zwischen den Modi konsistent sein soll. Natürlich möchten Sie diese Art von Verhalten sehr gut dokumentieren, damit die Benutzer Ihrer Bibliothek (auch wenn Sie es in drei Monaten sind!) Wissen, welche Überprüfungen sie durchführen sollen.

Wenn Sie jetzt eine Netzwerkkommunikationsbibliothek erstellen, möchten Sie so viel Sicherheit wie möglich hinzufügen, da 1) diese hauptsächlich von Benutzereingaben gespeist wird, daher Gefahr 2) die Rohleistung hauptsächlich durch Netzwerk-E/A begrenzt wird, In den meisten Fällen nicht durch CPU-Betrieb, daher wird das Hinzufügen einer Validierung nicht einmal bemerkt.

8
jv42

Jede Funktion sollte ihre Eingabe überprüfen, auch die internen Funktionen, die nicht Teil einer API oder einer öffentlichen Schnittstelle sind.

Programmierer sind Menschen, und Menschen sind bekanntermaßen nicht in der Lage, verwandte Einschränkungen synchron zu halten, wenn sich große Codebasen entwickeln - schließlich verschwindet der Teil "Eingabe überprüfen", der vor "Aufruffunktion" wird oder verschwindet an einen anderen Ort oder unvollständig werden, und die Funktion wird mit falscher Eingabe aufgerufen. In diesem Fall sind Ihre beiden wichtigsten Ziele:

  • Erkennen Sie das Problem so schnell wie möglich (Kompilierungszeit ist die beste Option).
  • Brechen Sie nichts, bis das Problem behoben ist

Für viele Dinge können Sie Sprachfunktionen oder das Typsystem verwenden, um Informationen zur Kompilierungszeit darüber zu übertragen, welche Eigenschaften überprüft wurden. Dies ist sowohl extrem schnell (keine Laufzeitstrafe) als auch erkennt Fehler beim Kompilieren. Die meisten meiner Überprüfungen befinden sich beispielsweise in dieser Kategorie.

Wenn Ihre Sprache die Überprüfung der Kompilierungszeit für Ihre Aktivitäten nicht unterstützt (was in modernen Sprachen recht selten vorkommt), fügen Sie eine Laufzeitzusicherung hinzu.

Nur wenn der Fehler Ihres Codes möglicherweise keine nachteiligen Folgen haben könnte, die über einen leicht zu erkennenden und harmlosen Fehler hinausgehen, und Sie erwarten, dass dieser Code extrem häufig aufgerufen wird, und die Überprüfung ist ohnehin kein natürlicher Bestandteil des Funktionscodes, Sie können die Überprüfungen weglassen. sqrt wäre wahrscheinlich hier.

2
Victor Nicollet

Für C/C++ und andere Sprachen, die einen funktionalen Präprozessor bereitstellen, können Sie Eingabeparameter nur beim Erstellen für Debugging/Test validieren und einen nicht validierten Release-Build erstellen.

Die Visual C++ MFC-Bibliothek ist eines der guten Beispiele.

Unten finden Sie den Beispielcode aus öffentlichen MFC-Beispielen:

void CServerNode::CalcBounding(CDC* pDC, CPoint& ptStart, CSize& sizeMax)
{
    ASSERT(sizeMax.cx >= 0 && sizeMax.cy >= 0);
    ASSERT(ptStart.x >= 0 && ptStart.y >= 0);

    CSize sizeNode;
    CalcNodeSize(pDC, sizeNode);

    ptStart.y += sizeNode.cy + CY_SEPARATOR;
    if (ptStart.y > sizeMax.cy)
        sizeMax.cy = ptStart.y;

    if (ptStart.x + sizeNode.cx > sizeMax.cx)
        sizeMax.cx = ptStart.x + sizeNode.cx;
    ptStart.x += CX_INDENT;
    // add in the kids
    if (!m_bHideChildren)
    {
        POSITION pos = m_listChild.GetHeadPosition();
        while (pos != NULL)
        {
            CServerNode* pNode = (CServerNode*)m_listChild.GetNext(pos);
            pNode->CalcBounding(pDC, ptStart, sizeMax);
        }
    }
    ptStart.x -= CX_INDENT;
}
2
9dan

Ich stelle die Validierung immer in die Nähe der Quelle eingehender Daten, egal ob es sich um Daten aus einer Datenbank, Daten aus einem HTTP POST oder Daten aus einem Netzwerk-Socket) handelt.

Die Gründe dafür sind:

  • reduzierung des Codes
  • beseitigung unnötiger Operationen
  • einfacherer Code (normalerweise)

Es wird jedoch immer Ausnahmen zu den meisten Programmierregeln oder Best Practices geben. Der Schlüssel zum Erkennen dieser Ausnahmen liegt in der sorgfältigen Überlegung und Berücksichtigung jeder Situation und nicht in der blinden Einhaltung eines Regelwerks.

1
dietbuddha

Ich denke, dass jede Funktion die Gültigkeit ihrer Eingabeparameter überprüfen sollte, und es ist besser, wenn sie zur Kompilierungszeit überprüft werden kann (mit Hilfe des Typsystems, wenn Sie eine statisch typisierte Sprache verwenden).

Die Idee, Assertions zu verwenden, um sicherzustellen, dass die Eingabeparameter gültig sind, erscheint mir ein wenig seltsam - im Wesentlichen bedeutet dies, dass Sie dieselben Prüfungen zweimal schreiben müssen - einmal in der Aufruferfunktion und das zweite Mal in der Methode selbst in Form von Behauptungen. Dies bedeutet auch, dass Sie bei Änderungen der Anforderungen an die Eingabeparameter die Überprüfungen überall in den aufrufenden Funktionen ändern müssen, nicht nur im Angerufenen.

Warum also nicht einfach die Parameter in der Methode selbst validieren und eine Ausnahme auslösen (oder was auch immer angemessen ist), wenn eine Inkonsistenz festgestellt wird?

1
user21125

Ich benutze immer das einfache Schema: Benutzeroberfläche und einfacher Code zum Aufrufen von Methoden (Validieren von UI-Parametern) -> Methoden (validiert keine UI-Parameter) -> Zusätzliche Funktionen (sie validieren nichts)

0
cnd