Viele Jahre des Codierens haben mich dazu gebracht zu glauben und zu versuchen, diese Art von konditionaler Codierung zu erreichen: (in C demonstriert, aber es ist fast für jede Sprache relevant)
%Vor%Was meiner Meinung nach viel lesbarer ist als das nächste Coding-Beispiel, besonders unter einer Vielzahl von Bedingungen:
%Vor%Während der obere Code viel besser lesbar ist, können Sie unter großen Bedingungen feststellen, dass Sie so viele "}" schließen, und der letzte Code, den ich glaube, ist zu einem besseren Code kompiliert.
Welche ist Ihrer Meinung nach besser zu benutzen?
(der Code in diesem Beispiel dient nur zum Demonstrieren, nehmen Sie ihn nicht wörtlich)
Wenn Sie Design by Contract befolgen, haben Sie Vorbedingungen, die erfüllt sein müssen, wenn die Funktion eingegeben wird. Obwohl es Situationen gibt, in denen bewiesen werden kann, dass einige Voraussetzungen bereits erfüllt sind, wenn die Funktion eingegeben wird und in diesen Fällen ein Test nicht notwendig ist, lassen Sie uns hier annehmen, dass Tests notwendig sind. Unter diesen Annahmen müssen funktions- vertragsmäßig die Voraussetzungen geprüft werden, bevor etwas anderes getan werden kann, und die Prüfungen sollten als unabhängig von der tatsächlichen Arbeit betrachtet werden, die die Funktion ausführen soll. Daraus folgt dann:
Die sofortige Rückkehr gewährleistet die vollständige Trennung von Vorbedingungstests vom eigentlichen Funktionskörper, nicht nur semantisch, sondern auch lexikalisch. Dies macht es einfach, den Code zu überprüfen und festzustellen, ob der Funktionsvertrag befolgt wird oder nicht. Es erleichtert außerdem das Hinzufügen oder Entfernen von Bedingungen später, wenn der Funktionsvertrag überarbeitet wird.
Beispielcode:
%Vor%Wenn Sie andererseits Design By Contract nicht befolgen, kann es vielleicht als eine Frage der persönlichen Präferenz angesehen werden.
Die Frage ist hier nicht wirklich klar. Wenn wir das vergleichen wollen:
%Vor%Damit:
%Vor%Dann würde ich für keines von beiden stimmen. Tun Sie dies stattdessen:
%Vor%Wenn die Frage ist, ob Mehrfachrückgaben akzeptabel sind oder nicht, dann wurde diese Frage bereits diskutiert (siehe: Sollte eine Funktion nur eine return-Anweisung haben )
Persönlich gehe ich mit dem ersten Abschnitt. Kehre so früh wie möglich zurück.
Der erste Ansatz sieht ästhetisch besser aus, aber ich denke nicht, dass er genau erfasst, was Sie zu tun versuchen. Wenn alle Bedingungen logisch zusammengehören, dann lege sie in eine if-Anweisung (ich setze meine booleschen Operatoren am Anfang einer Zeile, um das Auskommentieren einzelner Bedingungen während des Testens zu erleichtern (ein Überstreichen von großen SQL-WHERE-Klauseln). ):
%Vor%Oder wenn Sie einen booleschen Wert zurückgeben (was ein Sonderfall ist, der für Ihre Frage nicht zutrifft):
%Vor% Wenn die Bedingungen ähnliche oder verwandte Semantiken haben, ist es wahrscheinlich besser, einen logischen Ausdruck anstelle von if
s zu verwenden. Etwas wie
Aber wenn Sie if
verwenden müssen, hängt der beste Ansatz zum Organisieren der Verzweigung oft (wenn nicht immer) von der Art von if
selbst ab.
Meistens sind die if
s im Code "disbalanced": sie haben entweder nur eine Verzweigung (keine else
), oder eine der Verzweigungen ist "heavy", während die andere Verzweigung "light" ist . In solchen Fällen ist es immer besser, zuerst mit dem "einfachen" Zweig zu arbeiten und dann explizit anzugeben, dass die Verarbeitung für diesen Zweig beendet ist. Dies wird durch die Verwendung der ersten Variante aus Ihrer Frage erreicht: Erkennen Sie die Situation, die eine "einfache" Verarbeitung erfordert, tun Sie dies und sofort return
(wenn Sie die Funktion verlassen müssen) oder continue
(falls nötig) fahre mit der nächsten Iteration des Zyklus fort). Es ist diese unmittelbare return
(oder continue
), die dem Leser hilft zu verstehen, dass dieser Zweig getan ist und es keine Notwendigkeit mehr gibt, sich darum zu kümmern. Eine typische Funktion würde also wie folgt aussehen: ein Bündel von if
s, die die einfachen Fälle "abfangen", sie (falls erforderlich) und return
sofort behandeln. Und erst nachdem alle einfachen Fälle bearbeitet sind, beginnt die generische "schwere" Verarbeitung. Eine Funktion, die so organisiert ist, ist viel einfacher zu lesen, als die mit einer Menge verschachtelter if
s (wie Ihre zweite Variante).
Einige mögen argumentieren, dass return
s von der Mitte der Funktion oder continue
in der Mitte des Zyklus gegen die Idee der "strukturierten Programmierung" laufen. Aber irgendwie vermissen die Leute die Tatsache, dass das Konzept der "strukturierten Programmierung" nie für den praktischen Gebrauch gedacht war. Nein, das " return
early" -Prinzip (oder " continue
early" im Zyklus) verbessert die Lesbarkeit des Codes erheblich, weil explizit darauf hingewiesen wird, dass die Verarbeitung für diesen Zweig beendet ist. Im Fall von "strukturierten" if
s ist so etwas viel weniger offensichtlich.
So setzen Sie das oben genannte fort: Vermeiden Sie verschachtelte if
s. Sie sind schwer zu lesen. Vermeiden Sie "unausgeglichen" if
s. Sie sind auch schwer zu lesen. Bevorzugen Sie einen "flachen" Verzweigungsstil, der zuerst einfache Fälle behandelt und die Verarbeitung dann sofort beendet, indem Sie eine explizite return
oder continue
ausführen.
Ich wähle den zweiten Wunsch wäre besser, da es die Logik besser beschreibt, und es ist die beste Vorgehensweise, wenn Sie nur eine Return-Anweisung in einer Funktion haben.
Ich werde nicht die erste aus Gründen der Leistung wählen, da der Compiler die Optimierung sowieso tun wird, bevorzuge ich Code, der lesbar ist.
Ein Hauptgrund dafür, mit dem ersten Ansatz zu gehen, ist, dass die Einrückung in gesunden Ebenen bleibt. Alles jenseits von zwei Ebenen beginnt unhandlich zu werden. Persönlich versuche ich, eine einzelne Einrückungslevel innerhalb einer Methode die meisten Male nicht zu überschreiten.
Aber intuitiv, zumindest für mich, ist die zweite Wahl klarer, da die Logik einfach erscheint. Wenn alle drei Bedingungen erfüllt sind, geben wir wahr zurück. Ich neige dazu, jedes boolesche Ergebnis in einer Variablen zu speichern, deren Name Sinn macht, was der Wert vermittelt, und ihren UND-Wert zurückgibt.
%Vor%Ich habe die Namen nur anhand Ihres Beispiels erstellt, so dass sie wie Müll aussehen, aber der Punkt ist, dass sie nicht sollten.
Alternativ, wenn diese drei Variablen viel vorkommen, kombiniere ich sie zu einer einzigen Variablen, bevor ich sie zurückgebe.
%Vor%Soweit es mehrere Return-Anweisungen gibt, habe ich festgestellt, dass Rubys Art, eine Bedingung an die meisten Anweisungen zu suffixieren, sehr sauber und lesbar ist.
%Vor%Ihre zwei Beispiele machen unterschiedliche Dinge (siehe Code unten). Ich würde die zweite bevorzugen, wenn sie wie folgt geschrieben wäre (außer vielleicht mit weniger Klammern.
%Vor%oder wie
geschrieben %Vor%Um Ihre Frage zu beantworten. Ich bevorzuge die erste (außer der obige Code ist korrekt) und ich bevorzuge immer
Haben Sie einen kurzen Code am oberen und einen längeren Code am unteren Rand
if (cond) { Code(); } sonst wenn (cond2) { Code(); Code(); } sonst { Code(); Code(); Code(); }
Tags und Links code-review condition