Hier ist ein sehr vereinfachtes Beispiel . Dies ist nicht unbedingt eine sprachspezifische Frage, und ich fordere Sie auf, die vielen anderen Möglichkeiten zu ignorieren, mit denen die Funktion geschrieben werden kann, sowie die Änderungen, die daran vorgenommen werden können. . Die Farbe ist einzigartig
string CanLeaveWithoutUmbrella()
{
if(sky.Color.Equals(Color.Blue))
{
return "Yes you can";
}
else
{
return "No you can't";
}
}
Eine Menge Leute, die ich getroffen habe, ReSharper, und dieser Typ (dessen Kommentar mich daran erinnerte, dass ich das schon seit einiger Zeit fragen wollte) würden empfehlen, den Code umzugestalten, um den folgenden else
Block zu entfernen :
(Ich kann mich nicht erinnern, was die Mehrheit gesagt hat, ich hätte das vielleicht nicht anders gefragt.)
string CanLeaveWithoutUmbrella()
{
if(sky.Color.Equals(Color.Blue))
{
return "Yes you can";
}
return "No you can't";
}
Frage: Wird die Komplexität erhöht, wenn der else
Block nicht berücksichtigt wird?
Ich habe den Eindruck else
, dass der Code in beiden Blöcken direkt verwandt ist.
Außerdem finde ich, dass ich subtile Fehler in der Logik verhindern kann, insbesondere nach späteren Änderungen am Code.
Nehmen Sie diese Variante meines vereinfachten Beispiels (ignorieren Sie die Tatsache, dass der or
Operator ein absichtlich vereinfachtes Beispiel ist):
bool CanLeaveWithoutUmbrella()
{
if(sky.Color != Color.Blue)
{
return false;
}
return true;
}
Jemand kann jetzt einen neuen if
Block basierend auf einer Bedingung nach dem ersten Beispiel hinzufügen, ohne sofort richtig zu erkennen, dass die erste Bedingung eine Einschränkung für seine eigene Bedingung darstellt.
Wenn ein else
Block vorhanden war, wer auch immer die neue Bedingung , den Inhalt des bewegen zu gehen gezwungen sein würde , hinzugefügt else
Blockes (und wenn sie irgendwie beschönigen es Heuristik zeigt der Code nicht erreichbar ist, die es nicht im Fall einer tut if
beschränke andere) .
Natürlich gibt es auch andere Möglichkeiten, wie das spezifische Beispiel definiert werden sollte. All dies verhindert diese Situation, aber es ist nur ein Beispiel.
Die Länge des von mir angegebenen Beispiels kann den visuellen Aspekt verzerren. Nehmen wir also an, dass der Platz, der bis zu den Klammern beansprucht wird, für den Rest der Methode relativ unbedeutend ist.
Ich habe vergessen , einen Fall zu erwähnen , in dem ich mit dem Weglassen eines anderen Block einverstanden sind , und wird bei Verwendung eines if
Block eine Einschränkung anzuwenden, müssen logisch für erfüllt alle folgenden Code, wie eine Null-Prüfung (oder andere Wachen) .
quelle
else
Klausel besser lesbar ist (nach meinem Geschmack ist er sogar noch besser lesbar, wenn die nicht benötigten Klammern weggelassen werden. Aber ich denke, das Beispiel ist nicht gut gewählt, weil in der In dem obigen Fall würde ich schreibenreturn sky.Color == Color.Blue
(ohne if / else). Ein Beispiel mit einem anderen Rückgabetyp als bool würde dies wahrscheinlich klarer machen.Antworten:
Aus meiner Sicht ist der explizite else-Block vorzuziehen. Wenn ich das sehe:
Ich weiß, dass es sich um sich gegenseitig ausschließende Optionen handelt. Ich muss nicht lesen, was sich in den if-Blöcken befindet, um es zu erkennen.
Wenn ich das sehe:
Auf den ersten Blick sieht es so aus, als ob es trotzdem false zurückgibt, und der Himmel ist optional nicht blau.
Wie ich es sehe, spiegelt die Struktur des zweiten Codes nicht wider, was der Code tatsächlich tut. Das ist schlecht. Wählen Sie stattdessen die erste Option, die die logische Struktur widerspiegelt. Ich möchte nicht in jedem Block nach Return / Break / Continue-Logik suchen müssen, um den logischen Ablauf zu kennen.
Warum jemand den anderen bevorzugen würde, ist mir ein Rätsel, hoffentlich wird jemand die gegenteilige Position einnehmen und mich mit seiner Antwort aufklären.
Ich würde sagen, dass die Wachbedingungen in Ordnung sind, wenn Sie den glücklichen Pfad verlassen haben. Es ist ein Fehler aufgetreten, der verhindert, dass die normale Ausführung fortgesetzt wird. In diesem Fall sollten Sie eine Ausnahme auslösen, einen Fehlercode zurückgeben oder was auch immer für Ihre Sprache geeignet ist.
Kurz nach der ursprünglichen Version dieser Antwort wurde in meinem Projekt folgender Code entdeckt:
Indem die Rückgabe nicht innerhalb von elses platziert wurde, wurde die Tatsache übersehen, dass eine Rückgabeerklärung fehlte. Wäre noch jemand anders beschäftigt gewesen, hätte der Code nicht einmal kompiliert. (Die weitaus größere Sorge, die ich habe, ist natürlich, dass die Tests, die in diesem Code geschrieben wurden, ziemlich schlecht waren, um dieses Problem nicht zu erkennen.)
quelle
else
Blocks bevorzugen (es kann lästig sein, wenn ich dazu gezwungen bin, nur um die Konventionen für eine Codebasis einzuhalten). Ich bin auch daran interessiert, den Kontrapunkt hier zu sehen.} // else
das, was sonst als Kompromiss zwischen den beiden ginge.Der Hauptgrund für das Entfernen des
else
Blockes, den ich gefunden habe, ist übermäßiges Einrücken. Das Kurzschließen vonelse
Blöcken ermöglicht eine viel flachere Codestruktur.Viele
if
Aussagen sind im Wesentlichen Wachen. Sie verhindern die Dereferenzierung von Nullzeigern und andere "Tun Sie das nicht!" fehler. Und sie führen zu einer schnellen Beendigung der aktuellen Funktion. Beispielsweise:Nun mag es scheinen, dass eine
else
Klausel hier sehr vernünftig wäre:Und in der Tat, wenn das alles ist, was Sie tun, ist es nicht viel mehr eingerückt als das obige Beispiel. Dies ist jedoch selten alles, was Sie mit echten mehrstufigen Datenstrukturen tun (eine Bedingung, die bei der Verarbeitung gängiger Formate wie JSON, HTML und XML ständig auftritt). Wenn Sie also den Text aller untergeordneten Elemente eines bestimmten HTML-Knotens ändern möchten, sagen Sie:
Die Wachen erhöhen die Einrückung des Codes nicht. Mit einer
else
Klausel wird die gesamte Arbeit nach rechts verschoben:Dies beginnt eine signifikante Rechtsbewegung zu haben, und dies ist ein ziemlich triviales Beispiel mit nur zwei Schutzbedingungen. Jeder zusätzliche Wächter fügt eine weitere Einrückungsstufe hinzu. Echter Code, den ich geschrieben habe, um XML zu verarbeiten, oder der detaillierte JSON-Feeds verwendet, kann problemlos 3, 4 oder mehr Schutzbedingungen aufbauen. Wenn Sie die immer verwenden
else
, werden Sie 4, 5 oder 6 Ebenen eingerückt. Möglicherweise mehr. Und das trägt definitiv zur Komplexität des Codes bei, und es wird mehr Zeit darauf verwendet, zu verstehen, was mit was übereinstimmt. Die schnelle, flache Art des vorzeitigen Ausstiegs beseitigt einen Teil dieser "überschüssigen Struktur" und scheint einfacher zu sein.Nachtrag Die Kommentare zu dieser Antwort haben mich darauf aufmerksam gemacht, dass möglicherweise nicht klar war, dass die Behandlung mit NULL / Leer nicht der einzige Grund für Schutzbedingung ist. Nicht annähernd! Während sich dieses einfache Beispiel auf die Behandlung mit NULL / Leerzeichen konzentriert und keine anderen Gründe enthält, hat das Durchsuchen tiefer Strukturen wie XML und ASTs nach "relevanten" Inhalten häufig eine lange Reihe spezifischer Tests, um irrelevante und uninteressante Knoten auszusondern Fälle. Eine Reihe von "Wenn dieser Knoten nicht relevant ist, Rückgabetests" führt zu der gleichen Art von Rechtsabweichung wie NULL- / Leer-Guards. Und in der Praxis werden nachfolgende Relevanztests häufig mit NULL / Leer-Guards für die entsprechend tieferen Datenstrukturen gekoppelt - ein Doppelschlag für Rechtsdrift.
quelle
else
Block immer für überflüssig halte, wenn ich einenif
Block verwende, um eine Einschränkung anzuwenden, die für den gesamten folgenden Code logisch erfüllt sein muss , z. B. eine Nullprüfung (oder andere Schutzmaßnahmen).then
Klausel aussteigen können (z. B. aufeinanderfolgende Guard-Anweisungen), gibt es keinen besonderen Wert für die Vermeidung der jeweiligenelse
Klausel. In der Tat würde dies die Klarheit verringern und möglicherweise gefährlich sein (indem nicht einfach die alternative Struktur angegeben wird, die vorhanden ist / sein sollte).Wenn ich das sehe:
Ich sehe eine gemeinsame Idiom . Ein allgemeines Muster , das sich in meinem Kopf wie folgt übersetzt:
Da dies eine sehr verbreitete Programmiersprache ist, hat der Programmierer, der es gewohnt ist, diese Art von Code zu sehen, immer eine implizite "Übersetzung" in seinem Kopf, die ihn in die richtige Bedeutung "umwandelt".
Ich brauche das Explizite nicht
else
, mein Kopf „legt es dort hin“, weil er das Muster als Ganzes erkannt hat . Ich verstehe die Bedeutung des gesamten gemeinsamen Musters, daher benötige ich keine kleinen Details, um es zu verdeutlichen.Lesen Sie beispielsweise den folgenden Text:
Hast du das gelesen?
Wenn ja, liegen Sie falsch. Lese den Text erneut. Was tatsächlich geschrieben ist
Es gibt zwei „ die“ Wörter im Text. Warum haben Sie einen der beiden vermisst?
Es ist, weil Ihr Gehirn ein gemeinsames Muster sah und es sofort in seine bekannte Bedeutung übersetzte. Es musste nicht alles Detail für Detail durchgelesen werden, um die Bedeutung herauszufinden, da es das bereits erkannte Muster bereits beim Sehen . Deshalb ignorierte es das Extra "the ".
Dasselbe gilt für die obige Redewendung. Programmierer, die viel Code gelesen haben, sind es gewohnt, dieses Muster zu erkennen
if(something) {return x;} return y;
. Sie brauchen keine Erklärung, umelse
zu verstehen, was es bedeutet, denn ihr Gehirn „legt es für sie da“. Sie erkennen es als ein Muster mit einer bekannten Bedeutung: "Was nach der schließenden Klammer ist, wird nur implizitelse
von der vorherigen ausgeführtif
".Also meiner Meinung nach ist das
else
unnötig. Verwenden Sie jedoch nur das, was besser lesbar erscheint.quelle
Sie fügen dem Code visuelle Unordnung hinzu, also können sie die Komplexität erhöhen.
Das Gegenteil ist der Fall. Übermäßiges Refactoring zur Reduzierung der Codelänge kann jedoch auch die Komplexität erhöhen (natürlich nicht in diesem einfachen Fall).
Ich stimme der Aussage zu, dass der
else
Block die Absicht angibt. Mein Fazit ist jedoch anders, weil Sie Ihrem Code Komplexität hinzufügen, um dies zu erreichen.Ich bin mit Ihrer Aussage nicht einverstanden, dass Sie damit subtile Fehler in der Logik verhindern können.
Schauen wir uns ein Beispiel an, jetzt sollte es nur dann wahr sein, wenn der Himmel blau und die Luftfeuchtigkeit hoch ist, die Luftfeuchtigkeit nicht hoch ist oder wenn der Himmel rot ist. Aber dann verwechseln Sie eine Klammer und platzieren sie falsch
else
:Wir haben all diese dummen Fehler im Produktionscode gesehen und können sehr schwer zu erkennen sein.
Ich würde folgendes vorschlagen:
Ich finde das leichter zu lesen, weil ich auf einen Blick sehe, dass der Standardwert ist
false
.Die Idee, den Inhalt eines Blocks zu verschieben, um eine neue Klausel einzufügen, ist fehleranfällig. Dies hängt irgendwie mit dem Open / Closed-Prinzip zusammen (Code sollte für Erweiterungen offen, für Änderungen jedoch geschlossen sein). Sie müssen den vorhandenen Code ändern (z. B. kann der Codierer einen Fehler beim Ausgleichen von Klammern verursachen).
Schließlich führen Sie die Leute dazu, auf eine vorgegebene Weise zu antworten, die Sie für die richtige halten. Sie stellen zum Beispiel ein sehr einfaches Beispiel vor, erklären aber anschließend, dass die Leute es nicht berücksichtigen sollten. Die Einfachheit des Beispiels wirkt sich jedoch auf die gesamte Frage aus:
Wenn der Fall so einfach ist wie der vorgestellte, dann würde meine Antwort darin bestehen, irgendwelche
if else
Klauseln überhaupt wegzulassen .return (sky.Color == Color.Blue);
Wenn der Fall etwas komplizierter ist, würde ich mit verschiedenen Klauseln antworten:
bool CanLeaveWithoutUmbrella () {
}
Wenn der Fall kompliziert ist und nicht alle Klauseln true zurückgeben (möglicherweise wird ein double zurückgegeben) und ich einen Haltepunkt- oder einen Loggin-Befehl einführen möchte, würde ich Folgendes in Betracht ziehen:
}
Wenn es sich nicht um eine Methode handelt, die etwas zurückgibt, sondern um einen if-else-Block, der eine Variable setzt oder eine Methode aufruft, würde ich eine Schlussklausel
else
einfügen.Daher ist auch die Einfachheit des Beispiels zu berücksichtigen.
quelle
Obwohl der beschriebene If-else-Fall die größere Komplexität aufweist, spielt er in den meisten (aber nicht allen) praktischen Situationen keine große Rolle.
Als ich vor Jahren an Software arbeitete, die für die Luftfahrtindustrie verwendet wurde (sie musste einen Zertifizierungsprozess durchlaufen), stellte sich Ihre Frage. Es stellte sich heraus, dass diese "else" -Anweisung mit finanziellen Kosten verbunden war, da sie die Komplexität des Codes erhöhte.
Hier ist der Grund.
Das Vorhandensein des "Sonst" erzeugte einen impliziten dritten Fall, der ausgewertet werden musste - weder das "Wenn" noch das "Sonst", das ergriffen wurde. Sie könnten denken (wie ich zu der Zeit war) WTF?
Die Erklärung ging so ...
Aus logischer Sicht gibt es nur die beiden Möglichkeiten - das "Wenn" und das "Sonst". Was wir jedoch zertifizierten, war die Objektdatei, die der Compiler generierte. Wenn es also ein "sonst" gab, musste eine Analyse durchgeführt werden, um zu bestätigen, dass der generierte Verzweigungscode korrekt war und kein mysteriöser dritter Pfad auftauchte.
Stellen Sie dies dem Fall gegenüber, in dem es nur ein "Wenn" und kein "Sonst" gibt. In diesem Fall gibt es nur zwei Optionen: den Wenn-Pfad und den Nicht-Wenn-Pfad. Zwei Fälle sind weniger komplex als drei.
Hoffe das hilft.
quelle
Das wichtigste Ziel von Code ist es, verständlich als festgeschrieben zu sein (im Gegensatz zu leicht umgestaltbar, was nützlich, aber weniger wichtig ist). Ein etwas komplexeres Python-Beispiel kann verwendet werden, um dies zu veranschaulichen:
Dies ist ziemlich klar - es ist das Äquivalent einer
case
Anweisung oder einer Dictionary-Suche, der übergeordneten Version vonreturn foo == bar
:Dies ist klarer, denn obwohl die Anzahl der Token höher ist (32 gegenüber 24 für den ursprünglichen Code mit zwei Schlüssel / Wert-Paaren), ist die Semantik der Funktion jetzt explizit: Es handelt sich nur um eine Suche.
(Dies hat Konsequenzen für das Refactoring. Wenn Sie einen Nebeneffekt
key == NIK
haben möchten, haben Sie drei Möglichkeiten:if
/elif
/else
Stil und den Einsatz einer einzigen Zeile imkey == NIK
Fall.if
Anweisungvalue
für den Sonderfall hinzu.if
Erklärung in den Anrufer ein.Jetzt sehen wir, warum die Nachschlagefunktion eine mächtige Vereinfachung ist: Sie macht die dritte Option offensichtlich zur einfachsten Lösung, da sie nicht nur zu
value
einem geringeren Unterschied als die erste Option führt, sondern auch als einzige dafür sorgt, dass nur eine Sache getan wird. )Ich gehe auf den Code von OP zurück und denke, dies kann als Leitfaden für die Komplexität von
else
Anweisungen dienen: Code mit einer explizitenelse
Anweisung ist objektiv komplexer, aber wichtiger ist, ob dadurch die Semantik des Codes klar und einfach wird. Und das muss von Fall zu Fall beantwortet werden, da jeder Code eine andere Bedeutung hat.quelle
case
oderswitch
Aussagen) weniger homogen sind. Einige Optionen sind nur reine Wertrückgaben, aber ein oder zwei Fälle erfordern eine zusätzliche Verarbeitung. Und wenn Sie feststellen, dass die Suchstrategie nicht passt, müssen Sie eine völlig andereif
elif
...else
Syntax verwenden.Ich denke, es hängt davon ab, welche Art von
if
Aussage Sie verwenden. Einigeif
Anweisungen können als Ausdrücke betrachtet werden, andere nur als Kontrollflussanweisungen.Ihr Beispiel sieht für mich wie ein Ausdruck aus. In C-ähnlichen Sprachen
?:
ähnelt der ternäre Operator einerif
Anweisung, ist jedoch ein Ausdruck, und der else-Teil kann nicht weggelassen werden. Es ist sinnvoll, einen else-Zweig in einem Ausdruck nicht auszulassen, da der Ausdruck immer einen Wert haben muss.Mit dem ternären Operator würde Ihr Beispiel so aussehen:
Da es sich jedoch um einen booleschen Ausdruck handelt, sollte er wirklich vereinfacht werden
Das Einfügen einer expliziten else-Klausel macht Ihre Absicht klarer. Wächter können in manchen Situationen sinnvoll sein, aber wenn sie zwischen zwei möglichen Werten wählen, von denen keiner außergewöhnlich oder ungewöhnlich ist, helfen sie nicht.
quelle
I ask that you ignore the many other ways the function can be written, and changes that can be made to it. (I know most people are itching to write return sky.Color == Color.Blue.)
Eine andere Sache, über die man in diesem Argument nachdenken sollte, sind die Gewohnheiten, die jeder Ansatz fördert.
if / else formuliert ein Codierungsmuster in Form von Verzweigungen um. Wie Sie sagen, manchmal ist diese Aussage gut. Es gibt wirklich zwei mögliche Ausführungspfade und wir möchten das hervorheben.
In anderen Fällen gibt es nur einen Ausführungspfad und dann all die außergewöhnlichen Dinge, über die sich Programmierer Gedanken machen müssen. Wie Sie bereits erwähnt haben, sind Schutzklauseln hierfür hervorragend geeignet.
Es gibt natürlich die drei oder mehr Fälle (n), in denen wir normalerweise dazu ermutigen, die if / else-Kette zu verlassen und eine case / switch-Anweisung oder einen Polymorphismus zu verwenden.
Das Leitprinzip, an das ich hier denke, ist, dass eine Methode oder Funktion nur einen Zweck haben sollte. Jeder Code mit einer if / else- oder switch-Anweisung dient nur zur Verzweigung. Es sollte also absurd kurz sein (4 Zeilen) und nur an die richtige Methode delegieren oder das offensichtliche Ergebnis liefern (wie in Ihrem Beispiel). Wenn Ihr Code so kurz ist, ist es schwer, diese mehrfachen Rückgaben zu übersehen :) Ähnlich wie bei "als schädlich eingestuft", kann jede Verzweigungsanweisung missbraucht werden, daher lohnt es sich normalerweise, einige kritische Überlegungen in else-Anweisungen aufzunehmen. Wie Sie bereits erwähnt haben, bietet ein else-Block einen Platz, an dem sich Code ansammelt. Sie und Ihr Team müssen sich entscheiden, wie Sie das finden.
Worüber sich das Tool wirklich beschwert, ist die Tatsache, dass Sie ein return / break / throw innerhalb des if-Blocks haben. Was das betrifft, haben Sie eine Schutzklausel geschrieben, sie aber vermasselt. Sie können wählen, ob Sie das Tool glücklich machen oder seinen Vorschlag "unterhalten" möchten, ohne ihn zu akzeptieren.
quelle
else
semantische Aspekte nach. Code-Analyse-Tools suchen normalerweise nach fremden Anweisungen, da sie häufig auf ein Missverständnis des Kontrollflusses hinweisen. Daselse
after anif
, das zurückgibt, ist verschwendete Bits.if(1 == 1)
löst oft die gleiche Warnung aus.Ich denke, die Antwort hängt davon ab. Ihr Codebeispiel gibt (wie Sie indirekt betonen) einfach die Bewertung einer Bedingung zurück und wird, wie Sie offensichtlich wissen, am besten als einzelner Ausdruck dargestellt.
Um festzustellen, ob das Hinzufügen einer else-Bedingung den Code verdeutlicht oder verdeckt, müssen Sie bestimmen, was das IF / ELSE darstellt. Ist es (wie in Ihrem Beispiel) ein Ausdruck? In diesem Fall sollte dieser Ausdruck extrahiert und verwendet werden. Ist es eine Schutzbedingung? Wenn ja, dann ist das ELSE unnötig und irreführend, da es die beiden Zweige gleich erscheinen lässt. Ist es ein Beispiel für prozeduralen Polymorphismus? Extrahiere es. Setzt es Voraussetzungen? Dann kann es wichtig sein.
Bevor Sie sich entscheiden, das ELSE einzubeziehen oder zu eliminieren, entscheiden Sie, was es darstellt, und geben Sie an, ob es vorhanden sein soll oder nicht.
quelle
Die Antwort ist etwas subjektiv. Ein
else
Block kann die Lesbarkeit unterstützen, indem festgestellt wird, dass ein Codeblock ausschließlich für einen anderen Codeblock ausgeführt wird, ohne dass beide Blöcke gelesen werden müssen. Dies kann hilfreich sein, wenn beispielsweise beide Blöcke relativ lang sind. Es gibt jedoch Fälle, in denenif
s ohneelse
s besser lesbar sein kann. Vergleichen Sie den folgenden Code mit einer Anzahl vonif/else
Blöcken:mit:
Der zweite Codeblock wandelt die verschachtelten
if
Anweisungen in Schutzklauseln um. Dies reduziert die Einrückung und macht einige der Rückgabebedingungen klarer ("wenna != b
, dann kehren wir zurück0
!")In den Kommentaren wurde darauf hingewiesen, dass es in meinem Beispiel einige Löcher geben kann, deshalb werde ich es ein wenig weiter ausarbeiten.
Wir hätten den ersten Codeblock hier so ausschreiben können, um ihn lesbarer zu machen:
Dieser Code entspricht den beiden obigen Blöcken, birgt jedoch einige Herausforderungen. Die
else
Klausel hier verbindet diese if-Anweisungen miteinander. In der obigen Version der Schutzklausel sind die Schutzklauseln voneinander unabhängig. Wenn Sie eine neue hinzufügen, schreiben Sie einfach eine neueif
zwischen der letztenif
und der restlichen Logik. Dies ist auch hier mit nur geringfügig mehr kognitiver Belastung möglich. Der Unterschied besteht jedoch darin, dass vor dieser neuen Schutzklausel eine zusätzliche Logik erforderlich ist. Wenn die Aussagen unabhängig waren, konnten wir Folgendes tun:Mit der verbundenen
if/else
Kette ist das nicht so einfach. In der Tat wäre der einfachste Weg, die Kette zu durchbrechen, um mehr wie die Guard-Klausel-Version auszusehen.Aber das macht wirklich Sinn. Die
if/else
schwache Verwendung impliziert eine Beziehung zwischen Teilen. Wir könnten vermuten, dass dies in der verketteten Versiona != b
irgendwie verwandt ist . Diese Annahme wäre irreführend, wie wir aus der Fassung der Schutzklausel ersehen können, dass sie tatsächlich nicht miteinander zusammenhängen. Das bedeutet, dass wir mit unabhängigen Klauseln mehr tun können, als sie neu anzuordnen (vielleicht, um die Abfrageleistung zu optimieren oder den logischen Fluss zu verbessern). Wir können sie auch kognitiv ignorieren, wenn wir an ihnen vorbei sind. In einer Kette bin ich mir weniger sicher, dass die Äquivalenzbeziehung zwischen und später nicht wieder auftaucht.c > d
if/else
if/else
a
b
Die von implizierte Beziehung
else
wird auch im tief verschachtelten Beispielcode deutlich, jedoch auf andere Weise. Dieelse
Anweisungen sind von der Bedingung, an die sie gebunden sind, getrennt und in umgekehrter Reihenfolge mit den Bedingungen verknüpft (die ersteelse
ist an die letzte gebundenif
, die letzteelse
ist an die erste gebundenif
). Dies erzeugt eine kognitive Dissonanz, bei der wir den Code zurückverfolgen und versuchen müssen, die Einrückung in unserem Gehirn in eine Linie zu bringen. Es ist ein bisschen so, als würde man versuchen, ein paar Klammern zusammenzusetzen. Es wird noch schlimmer, wenn die Einrückung falsch ist. Optionale Zahnspangen helfen auch nicht.Dies ist eine nette Konzession, die aber keine Antwort ungültig macht. Ich könnte das in Ihrem Codebeispiel sagen:
Eine gültige Interpretation für das Schreiben von Code wie diesem könnte sein, dass "wir nur mit einem Regenschirm gehen müssen, wenn der Himmel nicht blau ist." Hier gibt es eine Einschränkung, dass der Himmel blau sein muss, damit der folgende Code ausgeführt werden kann. Ich habe die Definition Ihrer Konzession getroffen.
Was ist, wenn ich sage, wenn die Himmelsfarbe schwarz ist, ist es eine klare Nacht, daher ist kein Regenschirm erforderlich. (Es gibt einfachere Möglichkeiten, dies zu schreiben, aber es gibt einfachere Möglichkeiten, das gesamte Beispiel zu schreiben.)
Wie im obigen größeren Beispiel kann ich die neue Klausel einfach hinzufügen, ohne viel darüber nachzudenken. In einem
if/else
kann ich nicht so sicher sein, dass ich etwas nicht vermassle, besonders wenn die Logik komplizierter ist.Ich werde auch darauf hinweisen, dass Ihr einfaches Beispiel auch nicht so einfach ist. Ein Test für einen nicht-binären Wert ist nicht dasselbe wie das Inverse dieses Tests mit invertierten Ergebnissen.
quelle
else
Block weggelassen wird, ist dies für die Lesbarkeit von wesentlicher Bedeutung.Sie haben Ihr Beispiel zu stark vereinfacht. Entweder Alternative kann besser lesbar sein, abhängig von der größeren Situation: Insbesondere ist es hängt davon ab , ob einer der beiden Zweige des Zustandes abnormal . Lassen Sie sich zeigen: In einem Fall, in dem beide Branchen gleich wahrscheinlich sind, ...
... ist lesbarer als ...
... weil der erste parallel aufgebaut ist und der zweite nicht. Aber wenn der Großteil der Funktion einer Aufgabe gewidmet ist und Sie nur einige Voraussetzungen prüfen müssen, ...
}
... ist lesbarer als ...
... weil absichtlich nicht parallele Struktur der Einrichtung einen Hinweis auf einen zukünftigen Leser sieht vor, dass Eingang bekommen , die „wirklich gibt zwei“ hier ist abnormal . (Wäre im wirklichen Leben
ProcessInputDefinitelyOfTypeOne
keine separate Funktion, so wäre der Unterschied noch größer.)quelle
if
-> Mach das Außergewöhnliche und gehe « als Strategie für eine frühzeitige Rückkehr geschrieben werden sollte . Denken Sie an Code, in dem die Standardeinstellung mehr Überprüfungen enthält. Es wäre schneller, den Ausnahmefall zuerst zu behandeln.when using an if block to apply a constraint that must be logically satisfied for all following code, such as a null-check (or any other guards)
. Logischerweise hängt jede ArbeitProcessInputOfTypeOne
davon ab, dass!InputIsReallyTypeTwo(input)
wir sie außerhalb unserer normalen Verarbeitung abfangen müssen. NormalerweiseProcessInputOfTypeTwo(input);
wäre das wirklichthrow ICan'tProccessThatException
was die Ähnlichkeit deutlicher machen würde.u+
" normalerweise ein "Unicode-Range" -Token eingeführt. Wenn das nächste Zeichen jedoch keine Hex-Ziffer ist, dann muss man stattdessen das 'u' als Bezeichner sichern und verarbeiten. Die Haupt-Scannerschleife sendet den Befehl "Scannen eines Unicode-Bereichs-Tokens" etwas ungenau und beginnt mit "... wenn wir uns in diesem ungewöhnlichen Sonderfall befinden, sichern Sie sich, und rufen Sie stattdessen die Unterroutine" scan-an-identifier "auf.ProcessInputOfTypeOne
verwendet manchmal eine ungenaue, aber schnelle Überprüfung fängt stattdessen Typ zwei.Ja, die Komplexität nimmt zu, weil die else-Klausel überflüssig ist . Es ist daher besser, wegzulassen, um den Code schlank zu halten. Dies entspricht dem Weglassen redundanter
this
Qualifikationsmerkmale (Java, C ++, C #) und dem Weglassen von Klammern inreturn
Anweisungen und so weiter.Ein guter Programmierer denkt "Was kann ich hinzufügen?" während ein großartiger Programmierer denkt "Was kann ich entfernen?".
quelle
else
, dass der Block weggelassen wurde, und wenn er in einem Einzeiler erklärt wird (was nicht oft der Fall ist), dann geschieht dies oft mit dieser Art von Argumentation finde es stark genug zu argumentieren.A good programmer things "what can I add?" while a great programmer things "what can I remove?".
Es scheint ein allgemeines Sprichwort zu sein, dass viele Menschen ohne sorgfältige Überlegung überfordern, und ich persönlich finde, dass dies ein solcher Fall ist.Ich bemerkte, dass ich meine Meinung über diesen Fall änderte.
Wenn ich diese Frage vor ungefähr einem Jahr gestellt hätte, hätte ich etwas beantwortet wie:
» Zu einer Methode sollte es nur eins
entry
und eins gebenexit
« In diesem Sinne hätte ich vorgeschlagen, den Code zu überarbeiten, um:Aus Sicht der Kommunikation ist klar , was zu tun ist.
If
etwas ist der Fall, manipulieren Sie das Ergebnis auf eine Weise ,else
manipulieren Sie es auf eine andere Weise .Das ist aber unnötig
else
. Derelse
drückt den Standardfall aus . In einem zweiten Schritt könnte ich es also umgestaltenDann stellt sich die Frage: Warum überhaupt eine temporäre Variable verwenden? Der nächste Schritt der Refaktorisierung führt zu:
Das ist also klar, was es tut. Ich würde sogar noch einen Schritt weiter gehen:
Oder für schwache Nerven :
Man könnte es so lesen: »CanLeaveWithoutUmbrella gibt einen bool . Wenn nichts Besonderes passiert, wird false zurückgegeben «Dies ist der erste Lesevorgang von oben nach unten.
Und dann "parsen" Sie die Bedingung. »Wenn die Bedingung erfüllt ist, gibt sie true zurück. « Sie könnten die Bedingung vollständig überspringen und hätten verstanden, was diese Funktion im Standardfall tut. Ihr Auge und Ihr Verstand müssen nur einige Buchstaben auf der linken Seite durchblättern, ohne den Teil auf der rechten Seite zu lesen.
Ja, das ist richtig. Diese Informationen sind jedoch redundant . Sie haben einen Standardfall und eine "Ausnahme", die von der
if
-Anweisung .Beim Umgang mit komplexeren Strukturen würde ich eine andere Strategie wählen und den
if
oder den hässlichen Bruderswitch
überhaupt weglassen .quelle
So this is clear in what it does...
und erweitern? (Die vorgenannten Fälle sind ebenfalls von fragwürdiger Relevanz.) Ich sage das, weil es das ist, was die Frage ist, die wir untersuchen. Sie haben Ihre Haltung zum Ausdruck gebracht, den else-Block weggelassen, und es ist tatsächlich die Haltung, die näher erläutert werden muss (und die, die mich dazu gebracht hat, diese Frage zu stellen). Aus der Frage:I ask that you ignore the many other ways the function can be written, and changes that can be made to it. (I know most people are itching to write return sky.Color == Color.Blue.)
Um es kurz zu machen, in diesem Fall
else
ist es eine gute Sache, das Schlüsselwort loszuwerden . Es ist hier völlig überflüssig, und je nachdem, wie Sie Ihren Code formatieren, werden Ihrem Code ein bis drei völlig nutzlose Zeilen hinzugefügt . Überlegen Sie, was imif
Block enthalten ist:Daher wird, wenn der
if
Block eingegeben werden soll, der gesamte Rest der Funktion per Definition nicht ausgeführt. Wenn es jedoch nicht eingegeben wird,if
wird der gesamte Rest der Funktion ausgeführt , da keine weiteren Anweisungen vorhanden sind . Es wäre völlig anders, wenn einereturn
Aussage nicht imif
Block wäre, in welchem Fall das Vorhandensein oder Fehlen einerelse
Blocks Auswirkungen, aber hier hätte es keine Auswirkungen.In Ihrer Frage haben Sie nach dem Umkehren Ihrer
if
Bedingung und dem Weglassenelse
Folgendes angegeben:Dann müssen sie den Code etwas genauer lesen. Wir verwenden Hochsprachen und eine klare Organisation des Codes, um diese Probleme zu mindern. Durch Hinzufügen eines vollständig redundanten
else
Blocks wird der Code jedoch mit "Rauschen" und "Unordnung" versehen, und wir sollten auch unsereif
Bedingungen nicht invertieren oder erneut invertieren müssen. Wenn sie den Unterschied nicht erkennen können, wissen sie nicht, was sie tun. Wenn sie den Unterschied erkennen, können sie die ursprünglichenif
Bedingungen immer selbst umkehren .Das ist im Wesentlichen, was hier passiert. Sie kehren aus dem
if
Block zurück, sodass dieif
Bedingung, die als ausgewertetfalse
wird, eine Einschränkung ist, die für den gesamten folgenden Code logisch erfüllt sein muss.Nein, es wird eine Verringerung der Komplexität in Ihrem Code bedeuten, und es kann sogar eine Verringerung des kompilierten Codes bedeuten, je nachdem, ob bestimmte supertriviale Optimierungen vorgenommen werden oder nicht.
quelle
In dem konkreten Beispiel, das Sie angegeben haben, ist dies der Fall.
Einfacher geht es wohl nicht:
quelle
if
Aussage zu entfernen . Tatsächlich rate ich ausdrücklich von einer weiteren Vereinfachung ab, indem ich den genauen Code verwende, den Sie verwendet haben. Zusätzlich wird die zyklomatische Komplexität durch denelse
Block nicht erhöht .Ich versuche immer, meinen Code so zu schreiben, dass die Absicht so klar wie möglich ist. Ihrem Beispiel fehlt ein gewisser Kontext, deshalb werde ich ihn ein wenig modifizieren:
Unsere Absicht scheint zu sein dass wir ohne Regenschirm gehen können, wenn der Himmel blau ist. Diese einfache Absicht geht jedoch in einem Meer von Codes verloren. Es gibt verschiedene Möglichkeiten, die das Verständnis erleichtern.
Erstens gibt es Ihre Frage zu der
else
Branche. Mir macht es sowieso nichts aus, aber ich neige dazu, das wegzulassenelse
. Ich verstehe das Argument, explizit zu sein, aber ich bin der Meinung, dassif
Anweisungen 'optionale' Zweige wie denreturn true
einen einschließen sollten . Ich würde diereturn false
Verzweigung nicht als optional bezeichnen, da sie standardmäßig verwendet wird. In beiden Fällen sehe ich dies als sehr geringes Problem und weit weniger wichtig als die folgenden:Ein viel wichtigeres Problem ist, dass Ihre Filialen zu viel tun! Zweige sollten, wie alles, "so einfach wie möglich sein, aber nicht mehr". In diesem Fall haben Sie eine
if
Anweisung verwendet, die zwischen Codeblöcken (Auflistungen von Anweisungen) verzweigt, wenn Sie nur zwischen Ausdrücken (Werten) verzweigen müssen . Dies ist klar, da a) Ihre Codeblöcke nur einzelne Anweisungen enthalten und b) dieselbe Anweisung sind (return
). Wir können den ternären Operator verwenden?:
, um den Zweig auf den Teil einzugrenzen, der sich unterscheidet:Jetzt erreichen wir die offensichtliche Redundanz, mit der unser Ergebnis identisch ist
this.color == Color.Blue
. Ich weiß, dass Ihre Frage uns bittet, dies zu ignorieren, aber ich denke, es ist wirklich wichtig, darauf hinzuweisen, dass dies eine prozedurale Denkweise erster Ordnung ist: Sie zerlegen die Eingabewerte und konstruieren einige Ausgabewerte. Das ist in Ordnung, aber wenn möglich ist es viel mächtiger, in Beziehungen oder Transformationen zwischen Input und Output zu denken . In diesem Fall ist die Beziehung offensichtlich die gleiche, aber ich sehe oft einen verwickelten prozeduralen Code wie diesen, der eine einfache Beziehung verdeckt. Lassen Sie uns fortfahren und diese Vereinfachung vornehmen:Als nächstes möchte ich darauf hinweisen, dass Ihre API ein doppeltes Negativ enthält: Es ist möglich, dass dies
CanLeaveWithoutUmbrella
der Fall istfalse
. Das vereinfacht natürlich dasNeedUmbrella
Seintrue
, also lasst uns das machen:Nun können wir über Ihren Codierungsstil nachdenken. Es sieht so aus, als ob Sie eine objektorientierte Sprache verwenden. Wenn Sie jedoch
if
Anweisungen zum Verzweigen zwischen Codeblöcken verwenden, haben Sie am Ende prozeduralen Code geschrieben.Im objektorientierten Code sind alle Codeblöcke Methoden und alle Verzweigungen erfolgen über dynamisches Versenden , z. mit Klassen oder Prototypen. Es ist fraglich, ob dies die Dinge einfacher macht, aber es sollte Ihren Code konsistenter mit dem Rest der Sprache machen:
Beachten Sie, dass die Verwendung von ternären Operatoren zum Verzweigen zwischen Ausdrücken in der funktionalen Programmierung üblich ist.
quelle
I ask that you ignore the many other ways the function can be written, and changes that can be made to it. (I know most people are itching to write return sky.Color == Color.Blue.)
. In der Tat verwenden Sie die genaue Codezeile, die ich erwähne. Während dieser Teil der Frage vom Thema abweicht, würde ich außerdem sagen, dass Sie in Ihrer Schlussfolgerung zu weit gehen. Sie haben den Code grundlegend geändert.return false;
vselse { return false; }
während nicht Sorge um Zweig Polarität, dynamische Dispatch Ternäre, etc. Wenn Sie prozeduralen Code in einer OO - Sprache schreiben, radikale Veränderung notwendig ist ;)if/else
Anweisung zu tun haben soll und welche Verzweigungspolarität vorliegt.