Ich finde mich viel Code wie folgt zu schreiben:
int myFunction(Person* person) {
int personIsValid = !(person==NULL);
if (personIsValid) {
// do some stuff; might be lengthy
int myresult = whatever;
return myResult;
}
else {
return -1;
}
}
Es kann ziemlich chaotisch werden, insbesondere wenn mehrere Überprüfungen beteiligt sind. In solchen Fällen habe ich mit alternativen Stilen experimentiert, wie zum Beispiel diesem:
int netWorth(Person* person) {
if (Person==NULL) {
return -1;
}
if (!(person->isAlive)) {
return -1;
}
int assets = person->assets;
if (assets==-1) {
return -1;
}
int liabilities = person->liabilities;
if (liabilities==-1) {
return -1;
}
return assets - liabilities;
}
Ich bin an Kommentaren zu den hier getroffenen stilistischen Entscheidungen interessiert. [Sorgen Sie sich nicht zu sehr um die Details einzelner Aussagen; Es ist der gesamte Kontrollfluss, der mich interessiert.]
coding-style
language-agnostic
William Jockusch
quelle
quelle
Antworten:
Für diese Art von Fragen schlug Martin Fowler das Spezifikationsmuster vor :
Oben klingt ein bisschen hochnäsig (zumindest für mich), aber als ich es in meinem Code ausprobierte, lief es ziemlich reibungslos und erwies sich als einfach zu implementieren und zu lesen.
Nach meiner Auffassung besteht die Hauptidee darin, Code zu "extrahieren", der die Prüfungen in dedizierten Methoden / Objekten durchführt.
In Ihrem
netWorth
Beispiel könnte dies folgendermaßen aussehen:Ihr Fall scheint ziemlich einfach zu sein, sodass alle Überprüfungen so aussehen, dass sie in eine einfache Liste innerhalb einer einzelnen Methode passen. Ich muss oft auf mehrere Methoden aufteilen, um das Lesen zu verbessern.
Normalerweise gruppiere / extrahiere ich auch "spec" -bezogene Methoden in einem dedizierten Objekt, obwohl Ihr Fall ohne das in Ordnung aussieht.
Diese Frage bei Stack Overflow empfiehlt einige Links zusätzlich zu den oben genannten: Spezifikationsmuster-Beispiel . Insbesondere schlagen die Antworten Dimecasts 'Learning the Specification pattern' für eine exemplarische Darstellung eines Beispiels vor und erwähnen das von Eric Evans und Martin Fowler verfasste "Specifications" -Papier .
quelle
Ich finde es einfacher, die Validierung auf eine eigene Funktion zu verlagern. Sie trägt dazu bei, die Absichten anderer Funktionen klarer zu machen, sodass Ihr Beispiel so aussehen würde.
quelle
if
invalidPerson
? Kehren Sieperson!=NULL && person->isAlive && person->assets !=-1 && person->liabilities != -1
stattdessen einfach zurück .Eine Sache, die ich besonders gut gesehen habe, ist die Einführung einer Validierungsschicht in Ihren Code. Zuerst haben Sie eine Methode, die die gesamte fehlerhafte Überprüfung durchführt und Fehler zurückgibt (wie
-1
in Ihren obigen Beispielen), wenn etwas schief geht. Wenn die Validierung abgeschlossen ist, ruft die Funktion eine andere Funktion auf, um die eigentliche Arbeit auszuführen. Jetzt muss diese Funktion nicht alle diese Validierungsschritte ausführen, da sie bereits ausgeführt werden sollten. Das heißt, die Arbeitsfunktion nimmt an, dass die Eingabe gültig ist. Wie gehen Sie mit Annahmen um? Sie behaupten sie im Code.Ich denke, das macht den Code sehr einfach zu lesen. Die Validierungsmethode enthält den gesamten chaotischen Code, um Fehler auf der Benutzerseite zu behandeln. Die Arbeitsmethode dokumentiert ihre Annahmen sauber mit Zusicherungen und muss dann nicht mit potenziell ungültigen Daten arbeiten.
Betrachten Sie dieses Refactoring Ihres Beispiels:
quelle