Was definiert robusten Code?

42

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 Status, daher das "sonst".

Ich bin jedoch zweifelhaft. Wenn es sich bei der Variablen um einen Booleschen Wert handelt, wozu dient die Überprüfung eines dritten Zustands, wenn ein dritter Zustand logisch unmöglich ist?

"Einen Fehler nicht zu haben" scheint ebenfalls lächerlich; Sogar Google-Anwendungen zeigen dem Benutzer Fehler direkt an, anstatt sie unbemerkt 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 die Behauptung zu sein, dass eine Anwendung niemals Fehler enthalten würde.

Was ist nun die eigentliche Definition von "robustem Code"?

Lotus Notes
quelle
4
Dies würde nur in einer nicht stark typisierten Sprache gelten. In einer stark typisierten Sprache kann eine Variable vom Typ Boolean (keine Ganzzahl, die sich als Boolean ausgibt) nur wahr oder falsch sein, es gibt keine dritte Option ...
Marjan Venema
23
Fragen Sie ihn, wie Sie die Berichterstattung über den dritten Fall testen würden, da für robusten Code sicherlich Tests erforderlich sein sollten. Wenn Sie den dritten Fall nicht testen können, werden Sie möglicherweise keine Fehler finden, die darin lauern könnten.
gbjbaanb
2
@ Marjan - in einer nicht stark typisierten Sprache würde man höchstwahrscheinlich nur schreiben: if (var) {} else {}
Kevin Cline
2
Ich kenne keine Sprachen, in denen sowohl x als auch! X wahr sein könnten. Beachten Sie, dass ich nicht "if (x == true) ..." vorgeschlagen habe. Ich verabscheue solche Vergleiche.
Kevin Cline

Antworten:

33

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

Was ist Boolean?mit einem NULLZustand, der weder wahr noch falsch ist ? Was soll die Software nun tun? Manche Software muss hochgradig absturzsicher sein, beispielsweise Herzschrittmacher. Haben Sie schon einmal jemanden gesehen, der einer Datenbank eine Spalte hinzufügte, in der Booleandie aktuellen Daten initialisiert wurden NULL? 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 denken, dass es hier eine allgemein anerkannte Definition von "robust" gibt, dann viel Glück. Es kann einige Synonyme wie bombensicher oder idiotensicher geben. Der Duct Tape Programmer wäre ein Beispiel für jemanden, der normalerweise robusten Code schreibt, zumindest nach meinem Verständnis der Begriffe.

JB King
quelle
13
Wenn dies ein nullwertfähiger Boolescher Wert wäre, würden sowohl Java als auch C # diesen Wert auslösen.
Esben Skov Pedersen
Es scheint keine allgemein anerkannte Definition dafür zu geben, was eine Katze oder ein Hund sind.
Tulains Córdova
11

Für meine Diskussion kann ein Bool 2 Zustände haben, Wahr oder Falsch. Alles andere entspricht nicht der Programmiersprachenspezifikation. Wenn Ihre Werkzeugkette nicht der Spezifikation entspricht, werden Sie abgespritzt, egal was Sie tun. Wenn ein Entwickler eine Art Bool mit mehr als zwei Zuständen erstellt, ist dies das Letzte, was er jemals in 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 in der Regel sehr leicht zu erkennen, wenn Sie an sie denken. Das Beispiel, das Ihr Professor gegeben hat, kann nicht passieren, es ist also ein sehr schlechtes Beispiel.

A ist ohne gewundene Prüfgurte nicht zu testen. Wenn Sie es nicht erstellen können, wie werden Sie es testen? Wenn Sie den Code noch 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 mal an).

Option B ist trivial zu testen.

Stellen Sie Ihrem Professor als nächstes die 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 stört 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 schlimmer sein als ein Abbruch, der die Benutzer tötet. Was tun, wenn Sie wissen, dass die Antwort möglicherweise falsch ist? Entscheiden Sie sich für 50/50 oder brechen Sie ab und versuchen Sie es mit einem 100% igen Fehler. Wenn ich ein Besatzungsmitglied wäre, würde ich die 50/50 nehmen.

Option A tötet mich Option B gibt mir eine gleichmäßige Überlebenschance.

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

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

Angenommen, 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 Informationen - Therac-25-Röntgengerät, Ariane-5-Raketen-Fehler und andere (Link enthält viele defekte Links, aber genügend Informationen, die Google helfen wird)

mattnz
quelle
1
"..unerwartete Fehler. Sie sind in der Regel sehr leicht zu erkennen, wenn Sie an sie denken" - aber wenn Sie an sie denken, sind sie nicht mehr unerwartet.
gbjbaanb
7
Es gibt einige Fragen , ob Ihr Code stattdessen if (var != true || var != false) {ein sein &&sollte.
1
Ich kann mir leicht einen Dummkopf vorstellen, der weder wahr noch falsch ist, aber immer noch unerwartet. Wenn Sie sagen, ein Bool kann nichts anderes sein, wenn ich prüfe, ob ein Buchstabe ein Ziffernzeichen ist, und es dann in seinen ganzzahligen Wert umwandle, kann ich mir leicht vorstellen, dass dieser ganzzahlige Wert kleiner als 0 oder größer als 9 ist, aber es ist immer noch unerwartet.
gnasher729
1
NULL-Boolesche Werte werden in Java und C # unterstützt und verfügen über eine reale Anwendung. Stellen Sie sich eine Datenbank mit einer Liste von Personen vor. Nach einer Weile entscheiden Sie, dass Sie ein Feld für das Geschlecht (isMale) benötigen. Null bedeutet "nie gefragt, also nicht wissen"; wahr bedeutet männlich und falsch bedeutet weiblich. (OK, geschlechtsübergreifend der Einfachheit halber weggelassen ...).
Kiwiron
@kiwiron: Wäre eine bessere Lösung, wenn Sie nicht den Aufzählungstyp "Männlich", "Weiblich" oder "Nicht gefragt" verwenden. Aufzählungen sind besser - können bei Bedarf erweitert werden (zB bei Asexual, Hermaphrodite, "Refused to Answer" in den Sinn kommen).
Mattnz
9

Tatsächlich ist Ihr Code nicht robuster, aber WENIGER robust. Das Finale elseist einfach toter Code, den Sie nicht testen können.

In kritischer Software wie in Raumfahrzeugen ist toter Code und im Allgemeinen nicht getesteter Code verboten: Wenn ein kosmischer Strahl eine einzige 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.

mouviciel
quelle
Ich bekomme es nicht in Raumschiffen. Ist toter Code verboten? dh du kannst nicht das letzte schreiben sonst? seit du es nicht testen kannst kannst du es nicht reinlegen? aber was bedeutet es dann? "Wenn die SEU einen Teil des robusten Codes aktiviert, bleibt das (unerwartete) Verhalten unter Kontrolle."
Shabby
5
Ja, in platzkritischen Software-Tests muss die Abdeckung 100% betragen. Infolgedessen ist nicht erreichbarer Code (auch als toter Code bezeichnet) verboten.
mouviciel
7

Ich denke, der Professor könnte "Fehler" und "Fehler" verwechseln. Robuster Code sollte auf jeden Fall wenige / keine Fehler enthalten. 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 meins.

// 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!
David Andersson
quelle
1
Letzteres wäre sicherlich ausgelöst, es würde nicht wirklich so viel Mühe erfordern. Jeder erfahrene C-Programmierer hat festgestellt, dass sich die Werte plötzlich ändern. Natürlich sollte dies in einer kontrollierten Single-Thread-Umgebung niemals passieren. Im wirklichen Leben wird der Code im if irgendwann passieren. Wenn es nichts Nützliches gibt, was Sie in if tun können, dann codieren Sie es nicht! (Ich hatte eine lustige Erfahrung während einer bestimmten Software-Entwicklung, bei der ich eine Ausnahme mit Schimpfwörtern auslöste, falls etwas Unmögliches passierte ... raten Sie mal, was passierte?).
Alex
2
Wahre Begebenheit:boolean x = something(); if (x) { x = True // make sure it's really true, ... }
Andres F.
6

Es gibt keine vereinbarte Definition von Robust Code , da diese für viele Programmieraufgaben mehr oder weniger subjektiv ist ...

Das Beispiel, das Ihr Professor gibt, hängt von der Sprache ab:

  • In Haskell Booleankann a entweder sein Trueoder Falsees gibt keine dritte Option
  • In C ++, eine boolsein kann true, falseoder (leider) kommen aus irgendwelchen dubiosen Besetzung , die es in einem unbekannten Fall setzen ... Das sollte nicht passieren, kann aber als Folge eines vorangegangenen Fehlers.

Was Ihr Professor jedoch empfiehlt, verdeckt den Code, indem er in der Mitte des Kernprogramms eine fremde Logik für Ereignisse einführt, die nicht auftreten sollten , und ich werde Sie stattdessen auf die defensive Programmierung hinweisen .

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

  • Festlegen von Invarianten für Klassen (z. B. sizeAnzahl der Elemente in der dataListe)
  • Legen Sie Vor- und Nachbedingungen für jede Funktion fest (z. B. kann diese Funktion nur aufgerufen werden, awenn sie kleiner als ist 10).
  • Testen Sie diese an den Ein- und Ausstiegspunkten 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
Matthieu M.
quelle
Der Professor sagte jedoch ausdrücklich, dass es sich um Java handele, und sagte ausdrücklich NICHT, um welche Art von Var es sich handelt. Wenn es ein Boolescher Wert ist, kann er wahr, falsch oder null sein. Wenn etwas anderes, kann es ungleich wahr und ungleich falsch sein. Ja, Überlappung zwischen robust, defensiv und paranoid.
Andy Canfield
2
In C, C ++ und Objective-C kann ein Bool einen unbestimmten Wert haben, wie jeder andere Typ, aber jede Zuweisung setzt ihn auf wahr oder falsch und sonst nichts. Zum Beispiel: bool b = 0; b ++; b ++; setzt b auf true.
gnasher729
2

Der Ansatz Ihres Professors ist völlig falsch.

Eine Funktion oder nur ein Stück Code sollte eine Spezifikation haben, die besagt, was sie tut, und die jede mögliche Eingabe abdecken sollte. Und der Code sollte so geschrieben sein, dass sein Verhalten garantiert der Spezifikation entspricht. In dem 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: Sie besagt, dass die Funktion "das" tun soll. Schauen Sie sich den Code an: Die Funktion macht "das". Die Funktion entspricht der Spezifikation.

Wo der Code Ihres Professors die Dinge völlig unrobust macht, ist die Tatsache, dass bei 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 absolut unvorhersehbaren Ergebnissen.

gnasher729
quelle
1

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

Robust bedeutet, dass es bruch- und ausfallsicher ist, da es nur wenige Voraussetzungen erfüllt und entkoppelt ist: Es ist eigenständig, selbsterklärend und tragbar. Dazu gehört auch die Anpassung an sich ändernde Anforderungen. Kurz gesagt, Ihr Code ist dauerhaft .

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

Vektor
quelle
0

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

Es gibt viele Arten von Fehlern: falscher Code, unvollständiger Code, unerwartete Werte, unerwartete Zustände, Ausnahmen, Ressourcenerschöpfung, ... Robuster Code verarbeitet diese Probleme gut.

Sparky
quelle
0

Ich würde den von Ihnen angegebenen Code als Beispiel für defensive Programmierung betrachten (zumindest, wenn ich den Begriff verwende). Ein Teil der defensiven Programmierung besteht darin, Entscheidungen zu treffen, 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]
}

(Falls Sie den Unterschied nicht erkennen können, überprüfen Sie den Schleifentest: die erste Verwendung !=, die zweite Verwendung <).

In den meisten Fällen verhalten sich die beiden Schleifen jetzt genau gleich. Beim ersten (Vergleich mit !=) wird jedoch eine Annahme getroffen, idie nur einmal pro Iteration inkrementiert wird. Wenn der Wert übersprungen wird, kann sequence.length()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(Anmerkung: Tatsächlich wird immer noch die Annahme getroffen, dass dies iniemals negativ ist).

Stellen Sie sich vor, die Schleife scannt eine Zeichenfolge und führt eine Textverarbeitung durch, um eine Begründung dafür zu geben, 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. Ändern Sie daher den Schleifenkörper so, dass bei Erkennung eines Escape-Zeichens (z. B. Backslash) idas Zeichen unmittelbar nach dem Escape-Zeichen inkrementell übersprungen wird. Jetzt hat die erste Schleife einen Fehler, da der Schleifenkörper inkrementiert wird iund die Schleife über das Ende der Sequenz hinaus fortgesetzt wird, wenn das letzte Zeichen des Texts ein Backslash ist .

John Bartholomew
quelle
-1

Ich persönlich beschreibe einen Code als "robust", der folgende wichtige Attribute hat:

  1. Wenn meine Mutter davor sitzt und damit arbeitet, kann sie das System nicht kaputt machen

Mit break meine ich, entweder das System in einen instabilen Zustand zu versetzen oder eine UNHANDLED- Ausnahme auszulösen . Sie wissen, manchmal können Sie für ein einfaches Konzept eine komplexe Definition und Erklärung abgeben. Aber ich würde einfache Definitionen bevorzugen. Benutzer sind ziemlich gut darin, robuste Anwendungen zu finden. Wenn der Benutzer Ihrer Anwendung Ihnen viele Anfragen zu Fehlern, zu Statusverlusten, zu nicht intuitiven Workflows usw. sendet, liegt ein Fehler in Ihrer Programmierung vor.

Saeed Neamati
quelle