Frage

Disclaimer: Ich bin ein Laie derzeit Programm zu lernen. Nie Teil eines Projekts gewesen, noch nichts geschrieben länger als ~ 500 Zeilen.

Meine Frage ist: ist defensive Programmierung verletzen das Prinzip selbst nicht wiederholen Sie? Unter der Annahme, meine Definition von defensiver Programmierung richtig ist (mit dem anrufende Funktionseingang bestätigt anstelle der anderen), wäre das nicht, um Ihren Code schädlich sein?

Zum Beispiel ist das schlecht:

int foo(int bar)
{
    if (bar != /*condition*/)
    {
        //code, assert, return, etc.
    }
}

int main()
{
    int input = 10;
    foo(input); //doesn't the extra logic
    foo(input); //and potentially extra calls
    foo(input); //work against you?
}   

im Vergleich zu diesem:

int main()
{
    if (input == /*condition*/)
    {
        foo(input);
        foo(input);
        foo(input);
    }
}

Auch als Laie, ich weiß nicht, wie viel einfache Logik Aussagen gegen Sie zählen so weit wie Leistung geht, aber sicher defensive Programmierung ist für das Programm oder die Seele nicht gut.

War es hilfreich?

Lösung

Es kommt alles auf den Vertrag die Schnittstelle zur Verfügung stellt. Es gibt zwei verschiedene Szenarien für diese: Ein- und Ausgänge

.

Eingänge - und dass ich im Grunde mittlere Parameter an Funktionen -. Sollte durch die Umsetzung in der Regel geprüft werden

Ausgänge - wobei Rückkehr Ergebnisse -. Sollten grundsätzlich vom Anrufer vertraut werden, zumindest meiner Meinung nach

All dies wird durch diese Frage temperiert: Was passiert, wenn eine Partei den Vertrag bricht? Zum Beispiel kann sagen, dass Sie eine Schnittstelle haben:

class A {
  public:
    const char *get_stuff();
}

und dieser Vertrag legt fest, dass eine Null-Zeichenfolge wird nie zurückgegeben werden (es wird eine leere Zeichenfolge im schlimmsten Fall sein), dann ist es sicher ist, dies zu tun:

A a = ...
char buf[1000];
strcpy(buf, a.get_stuff());

Warum? Nun, wenn Sie falsch sind und die Angerufene ein auf null, dann wird das Programm zum Absturz bringen. Das ist eigentlich OK . Wenn ein Objekt seinen Vertrag verletzt dann das Ergebnis im Allgemeinen sollte katastrophal sein.

Die Risiko Sie Gesicht übermäßig in seinem Defensive ist, dass Sie eine Menge unnötigen Code zu schreiben (was mehr Fehler einführen kann), oder dass Sie könnte in der Tat ein ernstes Problem maskieren, indem eine Ausnahme zu schlucken, dass Sie wirklich nicht sollte.

Natürlich Umständen kann dies ändern.

Andere Tipps

sieht das DRY-Prinzip Verletzen wie folgt aus:

int foo(int bar)
{
    if (bar != /*condition*/)
    {
        //code, assert, return, etc.
    }
}

int main()
{
    int input = 10;
    if (input == /*condition*/)
    {
       foo(input);
       foo(input);
       foo(input);
    }
}

Wie Sie sehen können, ist das Problem, dass wir die gleiche Prüfung zweimal im Programm haben, also wenn der Zustand ändert, müssen wir es an zwei Stellen ändern, und die Chancen sind, dass wir einen von ihnen vergessen, was seltsames Verhalten. DRY bedeutet nicht „nicht ausführen, den gleichen Code zweimal“, aber „nicht schreibt den gleichen Code zweimal“

Lassen Sie mich Zustand zunächst, dass die unreflektierte ein Prinzip idealistisch ist und falsch. Sie müssen erreichen, was Sie (sagen wir, die Sicherheit Ihrer Anwendung) erreichen wollen, was wichtiger ist in der Regel weit ist, dass die Verletzung trocken. Vorsätzliche Verletzungen der Grundsätze sind meist notwendig, in GOOD-Programmierung.

Ein Beispiel: Ich mache doppelte Kontrollen an wichtigen Stationen (zB LoginService - erster Eingang einmal überprüfen, bevor LoginService.Login Aufruf und dann innen wieder), aber manchmal neige ich dazu, wieder die äußere entfernen später, nachdem ich dafür gesorgt, dass alles funktioniert 100%, in der Regel Unit-Tests verwenden. Es hängt davon ab.

Ich würde allerdings nie auf mehr als das Doppelte Zustand überprüft aufregen. sie ganz auf der anderen Seite VERGESSEN ist in der Regel mehrere Größenordnungen schlechter:)

Ich denke, defensive Programmierung Art eines schlechten Ruf bekommt, da es einige Dinge tut, die Art unerwünscht sind, die wortreich Code enthalten, und noch wichtiger, über Fehler tapezierte.

Die meisten Leute sind sich darüber einig, dass ein Programm sollte schnell fehlschlagen, wenn ein Fehler auftritt, sondern dass missionskritische Systeme sollten vorzugsweise nie versagen, und gehen stattdessen zu den großen Längen auf dem Gehen in das Gesicht von Fehlerzuständen zu halten.

Es gibt ein Problem mit dieser Aussage natürlich: Wie kann ein Programm, auch unternehmenskritische, fortgesetzt werden, wenn es in einem inkonsistenten Zustand. Natürlich kann es nicht wirklich.

Was Sie wollen, ist für das Programm alle vernünftigen Schritt zu tun, das Richtige zu tun, auch wenn es etwas seltsam los. Zugleich sollte das Programm beschweren, laut , jedes Mal wenn er trifft so einen ungeradeen Zustand. Und im Falle ein Fehler auftritt, die nicht wiederherstellbar ist, sollte es in der Regel eine HLT Anweisung vermeiden Ausgabe, sondern es sollte elegant ausfallen, heruntergefahren seine Systeme sicher oder ein Backup-System zu aktivieren, wenn eine verfügbar ist.

In Ihrem vereinfachtes Beispiel, ja, das zweite Format ist wahrscheinlich vorzuziehen.

Sie jedoch, dass nicht wirklich größer, komplexe und realistische Programme gilt.

Weil Sie nie im Voraus wissen, wo oder wie „foo“ verwendet wird, müssen Sie foo schützen, indem sie die Eingabe zu validieren. Wenn die Eingabe von dem Anrufer bestätigt wird (z. B. „main“ in Ihrem Beispiel), dann „main“ muss die Validierungsregeln kennen und anwenden sie.

In der realen Welt der Programmierung können die Eingabevalidierungsregeln ziemlich komplex sein. Es ist nicht angebracht, den Anrufer kennt alle Validierungsregeln und wendet sie richtig zu machen. Einige Anrufer, irgendwo, wird die Validierungsregeln vergessen, oder die falschen zu tun. So ist es besser, setzt die Validierung innerhalb „foo“, auch wenn es immer wieder genannt wird erhalten. Dadurch verschiebt sich die Last von dem Anrufer an den Angerufenen, die den Anrufer befreit weniger zu denken, über die Details von „foo“, und verwenden Sie es als eine abstrakte, zuverlässige Schnittstelle.

Wenn Sie wirklich ein Muster haben, wo „foo“ wird mehrmals mit der gleichen Eingabe aufgerufen, schlage ich vor, eine Wrapper-Funktion, die die Validierung einmal der Fall ist, und eine ungeschützte Version, die die Validierung Seiten Schritte:

void RepeatFoo(int bar, int repeatCount)
{
   /* Validate bar */
   if (bar != /*condition*/)
   {
       //code, assert, return, etc.
   }

   for(int i=0; i<repeatCount; ++i)
   {
       UnprotectedFoo(bar);
   }
}

void UnprotectedFoo(int bar)
{
    /* Note: no validation */

    /* do something with bar */
}

void Foo(int bar)
{
   /* Validate bar */
   /* either do the work, or call UnprotectedFoo */
}

Wie Alex gesagt, es hängt von der Situation, zum Beispiel I-Eingang bestätige fast immer in jeder Phase der Log-in-Prozess.

An anderen Orten, brauchen Sie nicht, dass alle.

Doch im Beispiel du hast, ich gehe davon aus, im zweiten Beispiel, dass Sie mehr als einen Eingang haben, denn sonst wird es überflüssig sein, die gleiche Funktion 3-mal für den gleichen Eingang aufrufen, was Sie bedeutet ‚ll die Bedingung 3 mal haben zu schreiben. Nun, das ist überflüssig.

Wenn der Eingang hat immer nur schließen sie in der Funktion überprüft werden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top