it-swarm.dev

Was macht robusten Code aus?

Mein Professor bezieht sich immer wieder auf dieses Java Beispiel, wenn er von "robustem" Code spricht:

if (var == true) {
    ...
} else if (var == false) {
    ...
} else {
    ...
}

Er behauptet, dass "robuster Code" bedeutet, dass Ihr Programm alle Möglichkeiten berücksichtigt und dass es keinen Fehler gibt - alle Situationen werden vom Code behandelt und führen zu einem gültigen Zustand, daher das "else".

Ich bin jedoch zweifelhaft. Wenn die Variable ein Boolescher Wert ist, was bringt es, einen dritten Zustand zu überprüfen, wenn ein dritter Zustand logisch unmöglich ist?

"Einen Fehler nicht zu haben" scheint ebenfalls lächerlich; Selbst Google-Anwendungen zeigen dem Benutzer Fehler direkt an, anstatt sie still zu verschlucken oder sie als gültigen Status zu betrachten. Und es ist gut - ich mag es zu wissen, wenn etwas schief geht. Und es scheint durchaus der Anspruch zu sein, zu sagen, dass eine Anwendung niemals Fehler enthalten würde.

Was ist also die tatsächliche Definition von "robustem Code"?

42
Lotus Notes

was bringt es, einen dritten Zustand zu überprüfen, wenn ein dritter Zustand logisch unmöglich ist?

Was ist mit einem Boolean?, das einen NULL -Zustand zulässt, der weder wahr noch falsch ist. Was soll die Software nun tun? Einige Software muss wie Herzschrittmacher sehr absturzsicher sein. Haben Sie jemals jemanden gesehen, der einer Datenbank, die ein Boolean war, eine Spalte hinzugefügt und die aktuellen Daten zunächst mit NULL initialisiert hat? Ich weiß, ich habe es gesehen.

Hier sind einige Links, die erläutern, was es bedeutet, in Bezug auf Software robust zu sein:

Wenn Sie der Meinung sind, dass es hier eine allgemein anerkannte Definition von "robust" gibt, viel Glück. Es kann einige Synonyme wie bombensicher oder idiotensicher geben. The Duct Tape Programmer wäre ein Beispiel für jemanden, der normalerweise robusten Code schreibt, zumindest nach meinem Verständnis der Begriffe.

33
JB King

Für meine Diskussion kann ein Bool zwei Zustände haben, Wahr oder Falsch. Alles andere entspricht nicht der Programmiersprache. Wenn Ihre Werkzeugkette nicht den Spezifikationen entspricht, werden Sie abgespritzt, egal was Sie tun. Wenn ein Entwickler einen Bool-Typ mit mehr als zwei Zuständen erstellt hat, ist dies das Letzte, was er jemals auf meiner Codebasis tun würde.

Option A.

if (var == true) {
    ...
} else if (var == false) {
    ...
} else {
    ...
}

Option B.

if (var == true) {
    ...
} else {
    ...
}

Ich behaupte, Option B ist robuster .....

Jeder Trottel kann Ihnen sagen, dass Sie mit unerwarteten Fehlern umgehen sollen. Sie sind normalerweise leicht zu erkennen, wenn Sie an sie denken. Das Beispiel, das Ihr Professor gegeben hat, kann nicht passieren, daher ist es ein sehr schlechtes Beispiel.

A ist ohne gewundene Testgurte nicht zu testen. Wenn Sie es nicht erstellen können, wie werden Sie es testen? Wenn Sie den Code nicht getestet haben, woher wissen Sie, dass er funktioniert? Wenn Sie nicht wissen, dass es funktioniert, schreiben Sie keine robuste Software. Ich denke, sie nennen das immer noch einen Catch22 (großartiger Film, schau ihn dir irgendwann an).

Option B ist trivial zu testen.

Als nächstes Problem stellen Sie Professor diese Frage: "Was soll ich dagegen tun, wenn ein Boolescher Wert weder wahr noch falsch ist?" Das sollte zu einer sehr interessanten Diskussion führen .....

In den meisten Fällen ist ein Core-Dump angemessen, im schlimmsten Fall nervt er den Benutzer oder kostet viel Geld. Was ist, wenn das Modul beispielsweise das Echtzeit-Wiedereintrittsberechnungssystem des Space Shuttles ist? Jede noch so ungenaue Antwort kann nicht schlechter sein als ein Abbruch, der die Benutzer tötet. Was tun, wenn Sie wissen, dass die Antwort möglicherweise falsch ist, wählen Sie 50/50 oder brechen Sie ab und gehen Sie zu 100% aus. Wenn ich ein Besatzungsmitglied wäre, würde ich die 50/50 nehmen.

Option A bringt mich um Option B gibt mir eine gleichmäßige Überlebenschance.

Aber warte - es ist eine Simulation des Wiedereintritts des Space Shuttles - was dann? Abbrechen, damit Sie davon erfahren. Klingt nach einer guten Idee? - NICHT - weil Sie mit dem Code testen müssen, den Sie versenden möchten.

Option A eignet sich besser für die Simulation, kann jedoch nicht bereitgestellt werden. Es ist nutzlos. Option B ist der bereitgestellte Code, sodass die Simulation die gleiche Leistung wie die Live-Systeme erbringt.

Nehmen wir an, dies war ein berechtigtes Anliegen. Die bessere Lösung wäre, die Fehlerbehandlung von der Anwendungslogik zu isolieren.

if (var != true || var != false) {
    errorReport("Hell just froze over, var must be true or false")
}
......
if (var == true){
 .... 
} else {
 .... 
}

Weitere Lesung - Therac-25-Röntgengerät, Ariane 5-Raketenausfall und andere (Link hat viele defekte Links, aber genug Informationen, die Google helfen wird)

11
mattnz

Eigentlich ist Ihr Code nicht robuster, aber WENIGER robust. Das letzte else ist einfach toter Code, den Sie nicht testen können.

In kritischer Software wie in Raumfahrzeugen ist toter Code und allgemein ungetesteter Code verboten: Wenn ein kosmischer Strahl eine einzelne Ereignisstörung hervorruft, die wiederum dazu führt, dass Ihr toter Code aktiviert wird, ist alles möglich. Wenn die SEU einen Teil des robusten Codes aktiviert, bleibt das (unerwartete) Verhalten unter Kontrolle.

9
mouviciel

Ich denke, der Professor könnte "Fehler" und "Fehler" verwechseln. Robuster Code sollte auf jeden Fall wenige/keine Fehler aufweisen. Robuster Code kann und muss in einer feindlichen Umgebung ein gutes Fehlermanagement aufweisen (sei es Ausnahmebehandlung oder strenge Rückgabestatus-Tests).

Ich stimme zu, dass das Codebeispiel des Professors albern ist, aber nicht so albern wie meines.

// Assign 3 to x
var x = 3;
x = 3;   // again, just for sure
while (x < 3 or x > 3) { x = 3; }  // being robust
if (x != 3) { ... }  // this got to be an error!
7
David Andersson

Es gibt keine vereinbarte Definition von Robust Code , da es für viele Dinge in der Programmierung mehr oder weniger subjektiv ist ...

Das Beispiel Ihres Professors hängt von der Sprache ab:

  • In Haskell kann ein Boolean entweder True oder False sein, es gibt keine dritte Option
  • In C++ kann ein booltrue, false sein oder (leider) aus einer zweifelhaften Besetzung stammen, die es in einen unbekannten Fall gebracht hat ... This sollte nicht passieren, kann aber aufgrund eines vorherigen Fehlers auftreten.

Was Ihr Professor jedoch empfiehlt, verdeckt den Code, indem er eine Fremdlogik für Ereignisse, die nicht auftreten sollten in der Mitte des Kernprogramms einführt, also werde ich es tun Zeigen Sie stattdessen auf Defensive Programming .

Im Falle einer Universität können Sie diese sogar durch eine Design By Contract-Strategie erweitern:

  • Erstellen Sie Invarianten für Klassen (z. B. size ist die Anzahl der Elemente in der Liste data).
  • Legen Sie Vor- und Nachbedingungen für jede Funktion fest (z. B. kann diese Funktion nur aufgerufen werden, wenn a kleiner als 10 Ist).
  • Testen Sie alle an den Eingangs- und Ausgangspunkten jeder Ihrer Funktionen

Beispiel:

class List:
  def __init__(self, items):
    self.__size = len(items)
    self.__data = items

  def __invariant(self):
    assert self.__size == len(self.__data)

  def size(self):
    self.__invariant()

    return self.__size

  def at(self, index):
    """index should be in [0,size)"""
    self.__invariant()
    assert index >= 0 and index < self.__size

    return self.__data[index]

  def pushback(self, item):
    """the subsequent list is one item longer
       the item can be retrieved by self.at(self.size()-1)"""
    self.__invariant()

    self.__data.append(item)
    self.__size += 1

    self.__invariant()
    assert self.at(self.size()-1) == item
6
Matthieu M.

Der Ansatz Ihres Professors ist völlig falsch.

Eine Funktion oder nur ein bisschen Code sollte eine Spezifikation haben, die angibt, was sie tut, und die jede mögliche Eingabe abdecken sollte. Und der Code sollte so geschrieben sein, dass sein Verhalten garantiert mit der Spezifikation übereinstimmt. Im Beispiel würde ich die Spezifikation ganz einfach so schreiben:

Spec: If var is false then the function does "this", otherwise it does "that". 

Dann schreiben Sie die Funktion:

if (var == false) dothis; else dothat; 

und der Code entspricht der Spezifikation. Ihr Professor sagt also: Was ist, wenn var == 42? Schauen Sie sich die Spezifikation an: Es heißt, die Funktion sollte "das" tun. Schauen Sie sich den Code an: Die Funktion macht "das". Die Funktion entspricht der Spezifikation.

Wenn der Code Ihres Professors die Dinge völlig unrobust macht, ist die Tatsache, dass mit seinem Ansatz, wenn var weder wahr noch falsch ist, Code ausgeführt wird, der noch nie zuvor aufgerufen wurde und der völlig ungetestet ist, mit völlig unvorhersehbaren Ergebnissen.

2
gnasher729

Ich stimme der Aussage von @ gnasher729 zu: Der Ansatz Ihres Professors ist völlig falsch.

Robust bedeutet, dass es bruchsicher ist, da es nur wenige Annahmen trifft und entkoppelt ist: eigenständig, selbstdefinierend und tragbar. Dazu gehört auch die Anpassung an sich ändernde Anforderungen. In einem Wort ist Ihr Code dauerhaft.

Dies führt im Allgemeinen zu kurzen Funktionen, deren Daten aus vom Aufrufer übergebenen Parametern stammen, und zur Verwendung öffentlicher Schnittstellen für Verbraucher - abstrakte Methoden, Wrapper, Indirektion, Schnittstellen im COM-Stil usw. - anstelle von Funktionen, die konkreten Implementierungscode enthalten.

1
Vector

Robuster Code ist einfach Code, der Fehler gut behandelt. Nicht mehr und nicht weniger.

Es gibt viele Arten von Fehlern: falscher Code, unvollständiger Code, unerwartete Werte, unerwartete Zustände, Ausnahmen, Erschöpfung der Ressourcen, ... Robuster Code handhabt diese gut.

1
Sparky

Ich würde den Code, den Sie angegeben haben, als Beispiel für defensive Programmierung betrachten (zumindest, wenn ich den Begriff verwende). Ein Teil der defensiven Programmierung besteht darin, Entscheidungen zu treffen, die die Annahmen über das Verhalten des restlichen Systems minimieren. Welche davon ist zum Beispiel besser:

for (int i = 0; i != sequence.length(); ++i) {
    // do something with sequence[i]
}

Oder:

for (int i = 0; i < sequence.length(); ++i) {
    // do something with sequence[i]
}

(Wenn Sie Probleme haben, den Unterschied zu erkennen, überprüfen Sie den Schleifentest: Der erste verwendet !=, Der zweite verwendet <).

In den meisten Fällen verhalten sich die beiden Schleifen nun genauso. Beim ersten (im Vergleich zu !=) Wird jedoch davon ausgegangen, dass i nur einmal pro Iteration erhöht wird. Wenn der Wert sequence.length() übersprungen wird, kann die Schleife über die Grenzen der Sequenz hinaus fortgesetzt werden und einen Fehler verursachen.

Sie können daher argumentieren, dass die zweite Implementierung robuster ist: Sie hängt nicht von Annahmen darüber ab, ob sich der Schleifenkörper ändert i (Hinweis: Tatsächlich wird immer noch davon ausgegangen, dass i niemals negativ ist ).

Stellen Sie sich vor, die Schleife scannt eine Zeichenfolge und führt eine Textverarbeitung durch, um zu motivieren, warum Sie diese Annahme möglicherweise nicht treffen möchten. Sie schreiben die Schleife und alles ist in Ordnung. Jetzt ändern sich Ihre Anforderungen und Sie entscheiden, dass Sie Escape-Zeichen in der Textzeichenfolge unterstützen müssen. Sie ändern also den Schleifenkörper so, dass er i erhöht, wenn ein Escape-Zeichen (z. B. Backslash) erkannt wird, um das Zeichen zu überspringen unmittelbar nach der Flucht. Jetzt hat die erste Schleife einen Fehler, denn wenn das letzte Zeichen des Textes ein umgekehrter Schrägstrich ist, erhöht der Schleifenkörper i und die Schleife wird über das Ende der Sequenz hinaus fortgesetzt.

0