Der Entwickler besteht darauf, dass Anweisungen keine negierten Bedingungen und immer einen else-Block enthalten

169

Ich habe einen Bekannten, einen erfahreneren Entwickler als ich. Wir sprachen über Programmierpraktiken und ich war überrascht von seiner Herangehensweise an Wenn-Aussagen. Er beharrt auf einigen Praktiken in Bezug auf Aussagen, die ich eher seltsam finde.

Zunächst sollte auf eine if-Anweisung eine else-Anweisung folgen, unabhängig davon, ob etwas darin enthalten ist oder nicht. Was dazu führt, dass der Code so aussieht:

if(condition) 
{
    doStuff();
    return whatever;
}
else
{
}

Zweitens ist es besser, auf wahre Werte zu prüfen, als auf falsche. Das bedeutet, dass es besser ist, eine 'doorClosed'-Variable zu testen, als eine'! DoorOpened'-Variable

Sein Argument ist, dass es klarer macht, was der Code tut.

Was mich ziemlich verwirrt, da eine Kombination dieser beiden Regeln dazu führen kann, dass er diese Art von Code schreibt, wenn er etwas tun möchte, wenn die Bedingung nicht erfüllt ist.

if(condition)
{
}
else
{
    doStuff();
    return whatever;
}

Mein Gefühl dabei ist, dass es in der Tat sehr hässlich ist und / oder dass die Qualitätsverbesserung, falls vorhanden, vernachlässigbar ist. Aber als Junior neige ich dazu, an meinem Instinkt zu zweifeln.

Meine Fragen lauten also: Ist es eine gute / schlechte / "egal" Übung? Ist es üblich?

Patsuan
quelle
57
Ist es möglich, dass Ihr Kollege aus einem Umfeld stammt, in dem es um Leib und Leben gehen könnte? Ich glaube, ich habe einige Erwähnungen dieser Art in Systemen gesehen, in denen unzählige Tests und automatische Analysen von Code durchgeführt werden und deren Verwechslung buchstäblich jemanden das Leben kosten könnte.
jpmc26
11
Was sagt Ihr Buchungskreis-Styleguide?
Thorbjørn Ravn Andersen
4
Teilweise Beantwortung Ihrer Frage "Zweitens". Wenn einer der Aktionsblöcke lang und der andere kurz ist, testen Sie eine Bedingung, bei der der kurze Block an erster Stelle steht, damit er beim Lesen des Codes weniger wahrscheinlich übersehen wird. Diese Bedingung kann eine Negation oder eine Umbenennung sein, um die Richtigkeit zu prüfen.
Ethan Bolker
9
Resharper hätte Ihrem Freund etwas mitzuteilen, etwa "Ihr Code ist redundant und nutzlos. Soll ich ihn löschen / umgestalten?"
BgrWorker

Antworten:

184

Expliziter elseBlock

Die erste Regel verschmutzt nur den Code und macht ihn weder lesbarer noch weniger fehleranfällig. Ich nehme an, Ihr Kollege hat das Ziel, explizit zu zeigen, dass sich der Entwickler voll und ganz bewusst war, dass die Bedingung möglicherweise als positiv bewertet wird false. Es ist zwar eine gute Sache, explizit zu sein, aber eine solche Aussage sollte nicht mit drei zusätzlichen Codezeilen verbunden sein .

Ich erwähne nicht einmal die Tatsache, dass auf eine ifAussage nicht unbedingt ein elseoder nichts folgt: Es können auch ein oder mehrere elifs folgen .

Das Vorhandensein der returnAussage macht die Sache noch schlimmer. Selbst wenn Sie tatsächlich Code zur Ausführung innerhalb des elseBlocks hätten, wäre es besser, dies so zu tun:

if (something)
{
    doStuff();
    return whatever;
}

doOtherThings();
return somethingElse;

Dies führt dazu, dass der Code zwei Zeilen weniger benötigt und den elseBlock rückgängig macht . In Guard-Klauseln geht es darum.

Beachten Sie jedoch, dass die Technik Ihres Kollegen ein sehr unangenehmes Muster aus gestapelten bedingten Blöcken ohne Leerzeichen teilweise lösen kann:

if (something)
{
}
if (other)
{
}
else
{
}

Im vorherigen Code ifmacht es das Fehlen eines vernünftigen Zeilenumbruchs nach dem ersten Block sehr leicht, den Code falsch zu interpretieren. Während die Regel Ihres Kollegen es schwieriger machen würde, den Code falsch zu lesen, besteht eine einfachere Lösung darin, eine neue Zeile hinzuzufügen.

Testen Sie für true, nicht fürfalse

Die zweite Regel mag sinnvoll sein, aber nicht in der aktuellen Form.

Es ist nicht falsch, dass das Prüfen auf eine geschlossene Tür intuitiver ist als das Prüfen auf eine nicht geöffnete Tür . Negationen und insbesondere verschachtelte Negationen sind normalerweise schwer zu verstehen:

if (!this.IsMaster || (!this.Ready && !this.CanWrite))

Um dies zu lösen, erstellen Sie , anstatt leere Blöcke hinzuzufügen, gegebenenfalls zusätzliche Eigenschaften oder lokale Variablen .

Die obige Bedingung könnte ziemlich leicht lesbar gemacht werden:

if (this.IsSlave || (this.Synchronizing && this.ReadOnly))
Arseni Mourzenko
quelle
169
Leere Blöcke sehen für mich immer wie Fehler aus. Es wird sofort meine Aufmerksamkeit auf sich ziehen und ich werde fragen: "Warum ist das hier?"
Nelson
50
@Neil Das Negieren einer Bedingung erfordert nicht unbedingt zusätzliche CPU-Zeit. C, das auf x86 kompiliert wurde, konnte einfach die Sprunganweisung von JZ(Jump if Zero) if (foo)nach JNZ(Jump if Not Zero) nach tauschen if (!foo).
8bittree
71
" Weitere Eigenschaften mit eindeutigen Namen erstellen ": Wenn Sie die Domäne nicht genauer analysieren, wird möglicherweise der globale Bereich (die ursprüngliche Klasse) geändert, um ein lokales Problem (die Anwendung einer bestimmten Geschäftsregel) zu lösen. Hier können Sie lokale Booleans mit dem gewünschten Status erstellen, z. B. "var isSlave =! This.IsMaster", und Ihr if mit den lokalen Booleans anwenden, anstatt mehrere andere Eigenschaften zu erstellen. Nehmen Sie sich also den Rat mit einem Körnchen Salz und nehmen Sie sich etwas Zeit, um die Domäne zu analysieren, bevor Sie neue Eigenschaften erstellen.
Machado
82
Es geht nicht um "drei Codezeilen", es geht darum, für das entsprechende Publikum zu schreiben (jemand mit mindestens einem grundlegenden Verständnis der Programmierung). Es ifwird bereits explizit darauf hingewiesen, dass die Bedingung falsch sein könnte oder dass Sie nicht darauf testen würden. Ein leerer Block macht die Dinge weniger klar, nicht mehr, weil kein Leser darauf vorbereitet ist, und jetzt müssen sie innehalten und darüber nachdenken, wie es dort hingekommen sein könnte.
Hobbs
15
@Mark, das ist weniger klar, auch mit dem Kommentar, als zu sagen if (!commonCase) { handle the uncommon case },.
Paul
62

In Bezug auf die erste Regel ist dies ein Beispiel für nutzloses Tippen. Das Schreiben dauert nicht nur länger, es führt auch zu großer Verwirrung bei jedem, der den Code liest. Wenn der Code nicht benötigt wird, schreiben Sie ihn nicht. Dies würde sogar dazu führen, dass elsein Ihrem Fall kein Eintrag vorhanden ist, da der Code vom ifBlock zurückgegeben wird:

if(condition) 
{
    doStuff();
    return whatever;
}

doSomethingElse(); // no else needed
return somethingelse;

In Bezug auf den zweiten Punkt ist es ratsam, boolesche Namen zu vermeiden, die ein Negativ enthalten:

bool doorNotOpen = false; // a double negative is hard to reason
bool doorClosed = false; // this is much clearer

Wenn Sie dies jedoch erweitern, um nicht erneut auf ein Negativ zu testen, führt dies, wie Sie betonen, zu einer unnötigeren Eingabe. Folgendes ist viel klarer als ein leerer ifBlock:

if(!condition)
{
    doStuff();
    return whatever;
}
David Arno
quelle
120
Also ... könnte man sagen, dass Doppel-Negativ ein Nein-Nein ist? ;)
Patsuan
6
@Patsuan, sehr schlau. Ich mag es. :)
David Arno
4
Ich habe das Gefühl, dass sich nicht viele Leute darüber einig sind, wie mit Wenn-Andern mit Retouren umgegangen werden soll, und manche sagen vielleicht sogar, dass es nicht in jedem Szenario gleich gehandhabt werden sollte. Ich habe selbst keine starke Vorliebe, aber ich kann definitiv das Argument hinter vielen anderen Möglichkeiten sehen, dies zu tun.
Dukeling
6
Klar, if (!doorNotOpen)ist schrecklich. Daher sollten die Namen so positiv wie möglich sein. if (! doorClosed)ist vollkommen in Ordnung.
David Schwartz
1
In Bezug auf Ihr Türbeispiel sind die beiden Beispielnamen nicht unbedingt gleichwertig. In der physischen Welt doorNotOpenist es nicht dasselbe wie doorClosedin einem Übergangszustand zwischen Offenheit und Geschlossenheit. Ich sehe dies die ganze Zeit in meinen industriellen Anwendungen, in denen boolesche Zustände durch physikalische Sensoren bestimmt werden. Wenn Sie einen einzelnen Sensor auf der offenen Seite der Tür haben, können Sie nur sagen, dass die Tür offen oder nicht offen ist. Befindet sich der Sensor ebenfalls auf der geschlossenen Seite, können Sie nur "geschlossen" oder "nicht geschlossen" sagen. Der Sensor bestimmt auch selbst, wie der logische Zustand bestätigt wird.
Peter M
45

1. Ein Argument für leere elseAussagen.

Ich benutze (und argumentiere) oft etwas Ähnliches wie dieses erste Konstrukt, ein leeres Anderes. Es signalisiert den Lesern des Codes (sowohl menschliche als auch automatisierte Analysewerkzeuge), dass der Programmierer über die Situation nachgedacht hat. Fehlende elseAussagen, die hätten vorliegen müssen, haben Menschen getötet, Fahrzeuge abgestürzt und Millionen von Dollar gekostet. Beispielsweise schreibt MISRA-C mindestens einen Kommentar vor, der besagt, dass das fehlende Finale else in einer if (condition_1) {do_this;} else if (condition_2) {do_that;} ... else if (condition_n) {do_something_else;}Sequenz beabsichtigt ist. Andere hohe Zuverlässigkeitsstandards gehen sogar noch weiter: Mit wenigen Ausnahmen sind fehlende else-Aussagen verboten.

Eine Ausnahme ist ein einfacher Kommentar, etwas in der Art von /* Else not required */. Dies signalisiert die gleiche Absicht wie die drei Zeilen, die sonst leer sind. Eine weitere Ausnahme, bei der dieses leere Element nicht benötigt wird, ist die Tatsache, dass es sowohl für Codeleser als auch für automatisierte Analysewerkzeuge offensichtlich ist, dass dieses leere Element überflüssig ist. Beispiel: if (condition) { do_stuff; return; }In ähnlicher Weise wird im Fall von throw somethingoder goto some_label1 anstelle von kein Leerzeichen "else" benötigt return.

2. Ein Argument für den Vorzug if (condition)vor if (!condition).

Dies ist ein menschlicher Faktor. Komplexe boolesche Logik stürzt viele Menschen in die Irre. Auch ein erfahrener Programmierer muss darüber nachdenken if (!(complex || (boolean && condition))) do_that; else do_this;. Schreiben Sie dies mindestens wie folgt um if (complex || (boolean && condition)) do_this; else do_that;.

3. Das heißt nicht, dass man leere thenAussagen bevorzugen sollte .

Der zweite Abschnitt sagt "lieber" als "du sollst". Es ist eher eine Richtlinie als eine Regel. Der Grund dafür, dass diese Richtlinie positive ifBedingungen bevorzugt , ist, dass Code klar und offensichtlich sein sollte. Eine leere then-Klausel (zB if (condition) ; else do_something;) verletzt dies. Es ist eine verschleierte Programmierung, die selbst die erfahrensten Programmierer dazu veranlasst, die ifBedingung in ihrer negierten Form zu sichern und erneut zu lesen . Schreiben Sie es also zunächst in negierter Form und lassen Sie die else-Anweisung weg (oder geben Sie ein leeres else oder einen Kommentar dazu, wenn Sie dazu aufgefordert werden).



1 Ich schrieb , dass dann Klauseln , die am Ende mit return, throwoder gotonicht ein leeres sonst erfordern. Es ist offensichtlich, dass die else-Klausel nicht benötigt wird. Aber was ist mit goto? Abgesehen davon verbieten sicherheitskritische Programmierregeln manchmal eine vorzeitige Rückkehr und verbieten fast immer das Auslösen von Ausnahmen. Sie erlauben jedoch gotoin eingeschränkter Form (zB goto cleanup1;). Diese eingeschränkte Verwendung gotoist an einigen Stellen die bevorzugte Praxis. Der Linux-Kernel zum Beispiel ist voll von solchen gotoAussagen.

David Hammen
quelle
6
!wird auch leicht übersehen. Es ist zu knapp.
Thorbjørn Ravn Andersen
4
Nein, ein leeres Anderes macht nicht klarer, dass der Programmierer darüber nachgedacht hat. Es führt zu mehr Verwirrung "Gab es einige geplante Aussagen und sie haben vergessen, oder warum gibt es eine leere Klausel?" Ein Kommentar ist viel klarer.
Simon
9
@ Simon: Eine typische Form ist else { /* Intentionally empty */ }oder etwas in diese Richtung. Das leere Sonst erfüllt den statischen Analysator, der sinnlos nach Regelverstößen sucht. Der Kommentar informiert den Leser darüber, dass das Leerzeichen "else" beabsichtigt ist. Andererseits lassen erfahrene Programmierer den Kommentar normalerweise als unnötig aus - aber nicht das leere Sonst. Hochzuverlässige Programmierung ist eine Domäne für sich. Die Konstrukte sehen für Außenstehende eher seltsam aus. Diese Konstrukte werden aufgrund von Lektionen aus der Schule der harten Schläge verwendet, Schläge, die sehr hart sind, wenn Leben und Tod oder $$$$$$$$$ zur Hand sind.
David Hammen
2
Ich verstehe nicht, wie leer then-Klauseln sind eine schlechte Sache, wenn leer else-Klauseln nicht sind (vorausgesetzt, wir wählen diesen Stil). Ich kann sehenif (target == source) then /* we need to do nothing */ else updateTarget(source)
Bergi
2
@ ThorbjørnRavnAndersen nein, notist ein Schlüsselwort in C ++, ebenso wie andere bitweise und logische Operatoren. Siehe alternative Operatordarstellungen .
Ruslan
31

In sehr seltenen Fällen verwende ich einen leeren else-Zweig (und manchmal einen leeren if-Zweig): Wenn es offensichtlich ist, dass sowohl der if- als auch der else-Teil irgendwie behandelt werden sollten, der Fall jedoch aus nicht trivialen Gründen von behandelt werden kann nichts tun. Und deshalb würde jeder, der den Code mit der sonst erforderlichen Aktion liest, sofort den Verdacht haben, dass etwas fehlt, und seine Zeit verschwenden.

if (condition) {
    // No action needed because ...
} else {
    do_else_action()
}

if (condition) {
    do_if_action()
} else {
    // No action needed because ...
}

Aber nicht:

if (condition) {
    do_if_action()
} else {
    // I was told that an if always should have an else ...
}
gnasher729
quelle
5
Diese. Ich finde die Aussage if test succeeds -> no action needed -> else -> action neededsemantisch sehr unterschiedlich zur Aussage if it applies that the opposite of a test outcome is true then an action is needed. Ich erinnere mich an Martin Fowlers Zitat: "Jeder kann Code schreiben, den Computer verstehen können. Gute Programmierer schreiben Code, den Menschen verstehen können."
Tasos Papastylianou
2
@TasosPapastylianou Das ist Betrug. Das Gegenteil von 'wenn der Test erfolgreich ist -> keine Aktion erforderlich' ist 'wenn der Test nicht erfolgreich ist -> Aktion erforderlich'. Sie haben es mit ungefähr 10 Wörtern aufgefüllt.
Jerry B
@ JerryB es kommt auf den "Test" an. Manchmal ist Negation (sprachlich) unkompliziert, manchmal jedoch nicht und würde zu Verwirrung führen. In diesen Fällen ist es klarer, von "Negation / Gegenteil von wahrem Ergebnis" zu sprechen als von "Ergebnis nicht wahr". Der Punkt ist jedoch, dass es noch klarer wäre, einen 'leeren' Schritt einzuführen oder die Bedeutung der Variablennamen umzukehren. Dies ist typisch für "de morgan" Situationen: zB if ((missing || corrupt) && !backup) -> skip -> else do stuffist viel klarer (+ andere implizite Absichten) alsif ((!missing && !corrupt) || backup) -> do stuff
Tasos Papastylianou
1
@TasosPapastylianou Du könntest schreiben if(!((missing || corrupt) && !backup)) -> do stuffoder besser if((aviable && valid) || backup) -> do stuff. (In einem realen Beispiel müssen Sie jedoch überprüfen, ob das Backup gültig ist, bevor Sie es verwenden.)
12431234123412341234123
2
@TasosPapastylianou wo gibt es Doppelnegative in dem, was ich gesagt habe? Sicher, wenn Sie unverfälscht als Variablennamen verwenden würden, hätten Sie einen klaren Punkt. Obwohl , wenn Sie gemischte Booleschen Operatoren wie diese kommen, es kann besser sein , temporäre Variablen zu haben , in dem verwenden if: file_ok = !missing && !corrupt; if(file_ok || backup) do_stuff();was ich persönlich das Gefühl , macht es noch deutlicher.
Baldrickk
23

Wenn alles andere gleich ist, bevorzugen Sie die Kürze.

Was Sie nicht schreiben, muss niemand lesen und verstehen.

Obwohl es nützlich sein kann, explizit zu sein, ist dies nur dann der Fall, wenn ohne übermäßige Ausführlichkeit klar wird, dass das, was Sie geschrieben haben, wirklich das ist, was Sie schreiben wollten.


Vermeiden Sie leere Äste, sie sind nicht nur nutzlos, sondern auch ungewöhnlich und führen zu Verwirrung.

Vermeiden Sie es auch, einen else-Zweig zu schreiben, wenn Sie den if-Zweig direkt verlassen.

Eine nützliche Anwendung des expliziten Denkens wäre, einen Kommentar zu hinterlassen, wenn Sie durch Schalterfälle fallen // FALLTHRU, und einen Kommentar oder einen leeren Block zu verwenden, wenn Sie eine leere Aussage benötigen for(a;b;c) /**/;.

Deduplizierer
quelle
7
// FALLTHRUUgh, nicht in SCREAMING CAPS oder mit txtspk Abkürzungen. Ansonsten stimme ich zu und folge dieser Praxis selbst.
Cody Grey
7
@CodyGray - Dies ist ein Ort, an dem SCHREIEN eine gute Idee ist. Die meisten switch-Anweisungen enden jeweils mit break. Wenn dies nicht der Fall breakist, sind einige spektakuläre Softwarefehler aufgetreten. Ein Kommentar, der den Lesern des Codes mitteilt, dass das Durchfallen beabsichtigt ist, ist eine gute Sache. Dass statische Analysewerkzeuge nach diesem bestimmten Muster suchen und sich nicht über einen Sturz beschweren können, macht es noch besser.
David Hammen
1
@ Wossname: Ich habe gerade meine überprüft vim, und es erkennt keine Variation von FALLTHRU. Und ich denke, aus gutem Grund: TODOund FIXMEKommentare sind Kommentare, die griffbereit sein müssen, weil Sie in der Lage sein möchten, zu fragen, git grepwelche bekannten Fehler Sie in Ihrem Code haben und wo sie sich befinden. Ein Fallthrough ist kein Defekt, und es gibt keinen Grund, dafür zu greifen, was auch immer.
cmaster
4
@DavidHammen Nein, Schreien ist keine gute Idee: Wenn Sie anstelle einer erwarteten Pause einen // Durchbruch haben, sollte dies für jeden Entwickler ausreichen, um sofort zu verstehen. // Der Fallthrough spricht auch nicht von einem Defekt, sondern signalisiert lediglich, dass die Umstände berücksichtigt wurden und dass die Unterbrechung stattgefunden hat. was zu fehlen scheint, soll ja nicht dabei sein. Dies ist ein rein informativer Zweck und nichts, worüber man schreien müsste.
cmaster
1
@cmaster - Das Schreien ist keine gute Idee, ist deine Meinung. Die Meinungen anderer Menschen sind unterschiedlich. Und nur weil Ihre Konfiguration von vim nicht erkennt, FALLTHRUheißt das nicht, dass andere Benutzer vim nicht dafür konfiguriert haben.
David Hammen
6

Es gibt keine feste Regel über positive oder negative Bedingungen für eine IF-Anweisung, meines Wissens nicht. Ich persönlich bevorzuge die Codierung für einen positiven Fall anstelle eines negativen Falls. Ich werde dies jedoch mit Sicherheit nicht tun, wenn es mich dazu bringt, einen leeren IF-Block zu erstellen, gefolgt von einem ELSE-Block voller Logik. In einem solchen Fall würde es ungefähr 3 Sekunden dauern, bis der Test auf einen positiven Fall zurückgeführt wurde.

Was mir an Ihren Beispielen allerdings nicht gefällt, ist der völlig unnötige vertikale Platz, den das leere ELSE einnimmt. Es gibt ganz einfach keinen Grund, dies zu tun. Es fügt der Logik nichts hinzu, es hilft nicht, zu dokumentieren, was der Code tut, und es verbessert die Lesbarkeit überhaupt nicht. Tatsächlich würde ich argumentieren, dass der hinzugefügte vertikale Raum möglicherweise die Lesbarkeit beeinträchtigen könnte.

Langecrew
quelle
2
Dieser unnötige vertikale Abstand ergibt sich aus den Anforderungen, immer geschweifte Klammern zu verwenden, fehlende Elemente nicht zuzulassen und den Allman-Stil zum Einrücken zu verwenden.
David Hammen
Nun, ich würde denken, dass die Verwendung von geschweiften Klammern immer eine gute Sache ist, da dies die Absichten eines Entwicklers explizit macht - kein Raum für Rätselraten beim Lesen seines Codes. Das mag albern klingen, aber glauben Sie mir, besonders wenn Sie Junior-Entwickler betreuen, passieren Fehler im Zusammenhang mit fehlenden Zahnspangen. Ich persönlich war noch nie ein Fan von Einrückungen im Allman-Stil, aus genau dem Grund, den Sie erwähnen: unnötiger vertikaler Raum. Aber das ist nur meine Meinung und mein persönlicher Stil. Es ist nur das, was ich anfangs gelernt habe, und es hat sich immer angenehmer angefühlt, es zu lesen.
Langecrew
Persönlicher Stil: Unabhängig davon, welchen Klammerstil ich verwende (Allman, 1 TB, ...), verwende ich immer Klammern.
David Hammen
Komplexe Bedingungen oder solche, von denen Sie glauben, dass sie schwer zu verstehen sind, können fast immer gelöst werden, indem sie in ihre eigenen Methoden / Funktionen überführt werden. So etwas if(hasWriteAccess(user,file))könnte darunter komplex sein, aber auf einen Blick wissen Sie genau, was das Ergebnis sein sollte. Auch wenn es sich nur um ein paar Bedingungen handelt, z. B. if(user.hasPermission() && !file.isLocked())ein Methodenaufruf mit einem geeigneten Namen, wird der Punkt deutlich, sodass Negative weniger von Bedeutung sind.
Chris Schneider
Einverstanden.
Andererseits
5

Expliziter elseBlock

Ich bin damit nicht einverstanden, da dies eine pauschale Aussage ist, die alle Aussagen abdeckt if, aber es gibt Zeiten, in denen es gut ist, einen elseBlock aus Gewohnheit hinzuzufügen .

Eine ifAussage deckt meines Erachtens tatsächlich zwei unterschiedliche Funktionen ab.

Wenn wir etwas tun sollen, tun Sie es hier.

Sachen wie diese offensichtlich nicht nicht einen benötigen elseTeil.

    if (customer.hasCataracts()) {
        appointmentSuggestions.add(new CataractAppointment(customer));
    }
    if (customer.isDiabetic()) {
        customer.assignNurse(DiabeticNurses.pickBestFor(customer));
    }

und in einigen Fällen darauf bestehen, eine elsemögliche Irreführung hinzuzufügen .

    if (k > n) {
        return BigInteger.ZERO;
    }
    if (k <= 0 || k == n) {
        return BigInteger.ONE;
    }

ist nicht dasselbe wie

    if (k > n) {
        return BigInteger.ZERO;
    } else {
        if (k <= 0 || k == n) {
            return BigInteger.ONE;
        }
    }

obwohl es funktional das gleiche ist. Wenn Sie das erste ifmit einem Leerzeichen schreiben, erhalten elseSie möglicherweise das zweite Ergebnis, das unnötig hässlich ist.

Wenn wir nach einem bestimmten Zustand suchen, ist es oft eine gute Idee, ein Leerzeichen hinzuzufügen else, um Sie daran zu erinnern, dass diese Möglichkeit besteht

        // Count wins/losses.
        if (doors[firstChoice] == Prize.Car) {
            // We would have won without switching!
            winWhenNotSwitched += 1;
        } else {
            // We win if we switched to the car!
            if (doors[secondChoice] == Prize.Car) {
                // We picked right!
                winWhenSwitched += 1;
            } else {
                // Bad choice.
                lost += 1;
            }
        }

Denken Sie daran, dass diese Regeln nur gelten , wenn Sie neuen Code schreiben . IMHO Die leeren elseKlauseln sollten vor dem Einchecken entfernt werden.


Testen Sie für true, nicht fürfalse

Auch dies ist ein guter Rat auf allgemeiner Ebene, aber in vielen Fällen macht dies Code unnötig komplex und weniger lesbar.

Auch wenn Code gefällt

    if(!customer.canBuyAlcohol()) {
        // ...
    }

scherzt den Leser, macht es aber

    if(customer.canBuyAlcohol()) {
        // Do nothing.
    } else {
        // ...
    }

ist mindestens so schlimm, wenn nicht schlimmer.

Ich habe vor vielen Jahren in BCPL codiert und in dieser Sprache gibt es eine IFKlausel und eine UNLESSKlausel, so dass Sie viel besser lesbar codieren können als:

    unless(customer.canBuyAlcohol()) {
        // ...
    }

das ist deutlich besser, aber immer noch nicht perfekt.


Mein persönlicher Prozess

Wenn ich neuen Code schreibe, füge ich häufig einen leeren elseBlock zu einer ifAnweisung hinzu, um mich daran zu erinnern, dass ich diese Möglichkeit noch nicht behandelt habe. Dies hilft mir, die DFSFalle zu umgehen , und stellt sicher, dass ich beim Überprüfen des Codes feststelle, dass noch mehr zu tun ist. Normalerweise füge ich jedoch einen TODOKommentar hinzu, um den Überblick zu behalten.

  if (returnVal == JFileChooser.APPROVE_OPTION) {
    handleFileChosen();
  } else {
    // TODO: Handle case where they pressed Cancel.
  }

Ich finde, dass ich elseim Allgemeinen selten in meinem Code verwende, da dies oft auf einen Codegeruch hinweisen kann.

OldCurmudgeon
quelle
Ihr Umgang mit „leeren“ Blöcke liest sich wie Standard Anstoßen aus Code , wenn thngs Umsetzung ...
Deduplicator
Der unlessBlock ist ein guter Vorschlag und beschränkt sich kaum auf BCPL.
Donnerstag,
@TobySpeight Ups. Es ist mir irgendwie entgangen, dass das, was ich geschrieben habe, ...tatsächlich war return. (In der Tat wird mit k=-1, n=-2die erste Rückkehr genommen, aber dies ist weitgehend umstritten.)
David Richerby
Modernes Perl hat ifund unless. Darüber hinaus werden diese nach einer Aussage auch zugelassen, sodass Sie sagen können, print "Hello world!" if $born;oder print "Hello world!" unless $dead;und beide tun genau das, was Sie glauben, dass sie tun.
ein Lebenslauf am
1

Für den ersten Punkt habe ich eine Sprache verwendet, die die Verwendung von IF-Anweisungen auf diese Weise erzwungen hat (in Opal, der Sprache, die sich hinter einem Bildschirmschaber für Großrechner verbirgt, um ein GUI-Front-End für Großrechnersysteme zu erstellen), und nur eine Zeile für die IF und die ELSE. Es war keine angenehme Erfahrung!

Ich würde erwarten, dass jeder Compiler solche zusätzlichen ELSE-Klauseln herausoptimiert. Für Live-Code wird jedoch nichts hinzugefügt (in der Entwicklung kann dies ein nützlicher Marker für weiteren Code sein).

Wenn ich etwas wie diese zusätzlichen Klauseln verwende, ist dies eine Zeit, in der ich CASE / WHEN-Typverarbeitung verwende. Ich füge immer eine Standardklausel hinzu, auch wenn sie leer ist. Dies ist eine langfristige Angewohnheit von Sprachen, die Fehler machen, wenn eine solche Klausel nicht verwendet wird, und einen Gedanken erzwingen, ob die Dinge wirklich einfach durchfallen sollten.

Vor langer Zeit wurde in der Mainframe-Praxis (z. B. PL / 1 und COBOL) akzeptiert, dass negative Prüfungen weniger effizient waren. Dies könnte den 2. Punkt erklären, obwohl es heutzutage massiv wichtigere Effizienzsteigerungen gibt, die als Mikrooptimierungen ignoriert werden.

Negative Logik ist in der Regel weniger lesbar, obwohl dies bei einer so einfachen IF-Anweisung weniger der Fall ist.

Kickstart
quelle
2
Ich sehe, so es war üblich , an einem gewissen Punkt.
Patsuan
@Patsuan - ja, bis zu einem gewissen Grad. In dieser Zeit war der Mainframe-Assembler eine ernstzunehmende Alternative für leistungskritischen Code.
Kickstart
1
"Vor langer Zeit ... wurde akzeptiert, dass negative Schecks weniger effizient waren." Nun, es sei denn, der Compiler optimiert if !A then B; else Cauf if A then C; else B. Das Berechnen der Negation der Bedingung ist eine zusätzliche CPU-Anweisung. (Obwohl, wie Sie sagen, ist es unwahrscheinlich, dass dies wesentlich weniger effizient ist.)
David Richerby
@DavidRicherby Und wenn wir im Allgemeinen sprechen, können einige Sprachen solche Optimierungen wirklich schwierig machen. Nehmen Sie C ++ mit überladenen !und ==Betreiber, zum Beispiel. Nicht, dass das jemand tun sollte , wohlgemerkt ...
ein Lebenslauf vom
1

Ich würde dem Standpunkt der meisten Antworten zustimmen, dass leere elseBlöcke praktisch immer eine schädliche Verschwendung von elektronischer Tinte sind. Fügen Sie diese nur hinzu, wenn Sie einen guten Grund dafür haben. In diesem Fall sollte der leere Block überhaupt nicht leer sein. Er sollte einen Kommentar enthalten, der erklärt, warum er dort ist.

Das Problem der Vermeidung von Negativen verdient jedoch etwas mehr Aufmerksamkeit: Wenn Sie einen booleschen Wert verwenden müssen, müssen in der Regel einige Codeblöcke ausgeführt werden, wenn er festgelegt ist, und einige andere Blöcke, wenn er nicht festgelegt ist. Wenn Sie als solches eine Regel ohne Negative erzwingen, erzwingen Sie entweder if() {} else {...}Anweisungen (mit einem leeren ifBlock!) Oder Sie erstellen einen zweiten Booleschen Wert für jeden Booleschen Wert, der seinen negierten Wert enthält. Beide Optionen sind schlecht, da sie Ihre Leser verwirren.

Eine hilfreiche Richtlinie lautet: Verwenden Sie niemals eine negierte Form innerhalb des Namens eines Booleschen und drücken Sie die Negation als eine einzige aus !. Eine Aussage wie if(!doorLocked)ist vollkommen klar, eine Aussage wie if(!doorUnlocked)Knotengehirne. Die spätere Art des Ausdrucks ist das, was Sie unbedingt vermeiden müssen, nicht das Vorhandensein einer Einzelperson !in einer if()Bedingung.

cmaster
quelle
0

Ich würde sagen, es ist definitiv eine schlechte Übung. Das Hinzufügen von else-Anweisungen fügt Ihrem Code eine ganze Reihe sinnloser Zeilen hinzu, die nichts bewirken und das Lesen noch schwieriger machen.

Ayrton Clark
quelle
1
Ich habe gehört, dass Sie noch nie für einen Arbeitgeber gearbeitet haben, der Ihre Leistung anhand der pro Zeitraum erstellten SLOCs bewertet ...
ein
0

Es gibt ein weiteres Muster, das noch nicht erwähnt wurde, um den zweiten Fall mit einem leeren if-Block zu behandeln. Eine Möglichkeit, damit umzugehen, wäre, im if-Block zurückzukehren. So was:

void foo()
{
    if (condition) return;

    doStuff();
    ...
}

Dies ist ein gängiges Muster zum Überprüfen von Fehlerbedingungen. Der zustimmende Fall wird immer noch verwendet, und das Innere ist nicht mehr leer, sodass zukünftige Programmierer sich fragen können, ob ein No-Op beabsichtigt war oder nicht. Dies kann auch die Lesbarkeit verbessern, da Sie möglicherweise eine neue Funktion erstellen müssen (wodurch große Funktionen in kleinere Einzelfunktionen unterteilt werden).

Shaz
quelle
0

Es gibt einen Punkt bei der Betrachtung des Arguments "Immer eine else-Klausel haben", den ich in keiner anderen Antwort gesehen habe: Es kann in einem funktionalen Programmierstil sinnvoll sein. Art von.

In einem funktionalen Programmierstil werden eher Ausdrücke als Anweisungen verwendet. Jeder Codeblock hat also einen Rückgabewert - einschließlich eines if-then-elseAusdrucks. Dies würde jedoch einen leeren elseBlock ausschließen. Lassen Sie mich ein Beispiel geben:

var even = if (n % 2 == 0) {
  return "even";
} else {
  return "odd";
}

Nun, in Sprachen mit C-Stil oder C-Stil inspirierter Syntax (wie Java, C # und JavaScript, um nur einige zu nennen) sieht dies seltsam aus. Es kommt mir jedoch viel bekannter vor, wenn man es so schreibt:

var even = (n % 2 == 0) ? "even" : "odd";

Wenn Sie den elseZweig hier leer lassen, wird ein Wert undefiniert. In den meisten Fällen ist dies kein gültiger Fall für die Programmierung von Funktionen. Das gleiche gilt für das völlige Auslassen. Wenn Sie jedoch iterativ programmieren, habe ich sehr wenig Grund, immer einen elseBlock zu haben.

blalasaadri
quelle
0

... auf eine if-Anweisung sollte eine else-Anweisung folgen, unabhängig davon, ob etwas eingegeben werden muss oder nicht.

Ich bin nicht einverstanden, if (a) { } else { b(); }sollte umgeschrieben werden als if (!a) { b(); }und if (a) { b(); } else { }sollte umgeschrieben werden als if (a) { b(); }.

Es sei jedoch darauf hingewiesen, dass ich selten einen leeren Zweig habe. Das liegt daran, dass ich mich normalerweise anmelde, dass ich in den sonst leeren Zweig gegangen bin. Auf diese Weise kann ich rein aus Logmeldungen entwickeln. Ich benutze selten einen Debugger. In Produktionsumgebungen erhalten Sie keinen Debugger. Es ist schön, Produktionsprobleme mit denselben Tools beheben zu können, die Sie für die Entwicklung verwenden.

Zweitens ist es besser, auf wahre Werte zu prüfen, als auf falsche. Das bedeutet, dass es besser ist, eine 'doorClosed'-Variable zu testen, als eine'! DoorOpened'-Variable

Ich habe diesbezüglich gemischte Gefühle. Ein Nachteil von doorClosedund doorOpenedist, dass sich die Anzahl der Wörter / Begriffe, die Sie kennen müssen, möglicherweise verdoppelt. Ein weiterer Nachteil ist, dass sich mit der Zeit die Bedeutung von doorClosedund doorOpenedmöglicherweise ändert (andere Entwickler folgen Ihnen) und Sie möglicherweise zwei Eigenschaften erhalten, bei denen es sich nicht mehr um präzise Negationen handelt. Anstatt Negationen zu vermeiden, lege ich Wert darauf, die Sprache des Codes (Klassennamen, Variablennamen usw.) an das Vokabular der Geschäftsbenutzer und die von mir gestellten Anforderungen anzupassen. Ich würde mir keinen neuen Begriff einfallen lassen wollen, um a!Wenn dieser Begriff nur für einen Entwickler eine Bedeutung hat, die Geschäftsbenutzer nicht verstehen. Ich möchte, dass Entwickler dieselbe Sprache sprechen wie die Benutzer und die Personen, die Anforderungen schreiben. Wenn nun neue Begriffe das Modell vereinfachen, dann ist das wichtig, aber das sollte behandelt werden, bevor die Anforderungen finalisiert sind, und nicht danach. Die Entwicklung sollte dieses Problem lösen, bevor sie mit dem Codieren beginnt.

Ich neige dazu, an meinem Instinkt zu zweifeln.

Es ist gut, sich weiter zu hinterfragen.

Ist es eine gute / schlechte / "egal" Übung?

Bis zu einem gewissen Grad spielt vieles keine Rolle. Lassen Sie Ihren Code überprüfen und an Ihr Team anpassen. Das ist normalerweise richtig. Wenn jeder in Ihrem Team eine bestimmte Art und Weise codieren möchte, ist es wahrscheinlich am besten, dies so zu tun (das Ändern einer Person - Sie selbst - erfordert weniger Story-Punkte als das Ändern einer Gruppe von Personen).

Ist es üblich?

Ich kann mich nicht erinnern jemals einen leeren Ast gesehen zu haben. Ich sehe Leute, die Eigenschaften hinzufügen, um Negationen zu vermeiden.

Wörter wie Jared
quelle
0

Ich werde nur den zweiten Teil der Frage ansprechen, und dies kommt aus meiner Sicht, in industriellen Systemen zu arbeiten und Geräte in der realen Welt zu manipulieren.

Dies ist jedoch in manchen Fällen eher ein ausführlicher Kommentar als eine Antwort.

Wenn es heißt

Zweitens ist es besser, auf wahre Werte zu prüfen, als auf falsche. Das bedeutet, dass es besser ist, eine 'doorClosed'-Variable zu testen, als eine'! DoorOpened'-Variable

Sein Argument ist, dass es klarer macht, was der Code tut.

Aus meiner Sicht wird hier die Annahme getroffen, dass der Türzustand ein binärer Zustand ist, obwohl es sich tatsächlich um mindestens einen ternären Zustand handelt:

  1. Die Tür ist offen.
  2. Die Tür ist weder ganz offen noch ganz geschlossen.
  3. Die Tür ist geschlossen.

Je nachdem, wo sich ein einzelner binärer Digitalsensor befindet, können Sie also nur eines von zwei Konzepten vermuten:

  1. Wenn sich der Sensor auf der offenen Seite der Tür befindet: Die Tür ist entweder offen oder nicht offen
  2. Wenn sich der Sensor auf der geschlossenen Seite der Tür befindet: Die Tür ist entweder geschlossen oder nicht geschlossen.

Und Sie können diese Konzepte nicht durch die Verwendung einer binären Negation austauschen. Daher doorClosedund !doorOpenedwahrscheinlich auch nicht, und jeder Versuch, vorzutäuschen, dass es sich um ein Synonym handelt, ist falsches Denken, das eine größere Kenntnis des Systemzustands voraussetzt, als tatsächlich vorhanden ist.

Wenn ich auf Ihre Frage zurückkomme, würde ich eine Wortwahl unterstützen, die dem Ursprung der Informationen entspricht, die die Variable darstellt. Wenn die Informationen von der geschlossenen Seite der Tür stammen, doorClosedkann dies bedeuten, dass je nach Bedarf eine doorClosedoder !doorClosedmehrere Aussagen verwendet werden, was zu einer möglichen Verwechslung mit der Verwendung der Verneinung führen kann. Aber was es nicht tut, ist implizit Annahmen über den Zustand des Systems zu verbreiten.


Zu Diskussionszwecken für die von mir geleistete Arbeit hängt die Menge der Informationen, die ich über den Zustand des Systems habe, von der Funktionalität ab, die das Gesamtsystem selbst benötigt.

Manchmal muss ich nur wissen, ob die Tür geschlossen oder nicht geschlossen ist. In diesem Fall ist nur ein einziger Binärsensor ausreichend. Aber zu anderen Zeiten muss ich wissen, dass die Tür offen, geschlossen oder im Übergang ist. In solchen Fällen gäbe es entweder zwei binäre Sensoren (bei jeder extremen Türbewegung - mit Fehlerprüfung, ob die Tür gleichzeitig geöffnet und geschlossen ist) oder einen analogen Sensor, der misst, wie „offen“ die Tür ist.

Ein Beispiel für die erste wäre die Tür eines Mikrowellenofens. Sie aktivieren den Betrieb des Ofens, wenn die Tür geschlossen oder nicht geschlossen ist. Es ist dir egal, wie offen die Tür ist.

Ein Beispiel für den zweiten wäre ein einfacher motorisch angetriebener Aktuator. Sie verhindern das Vorwärtsfahren des Motors, wenn der Antrieb vollständig ausgefahren ist. Und Sie sperren das Rückwärtsfahren des Motors, wenn der Stellantrieb vollständig eingefahren ist.

Die Anzahl und Art der Sensoren hängt im Wesentlichen davon ab, ob eine Kostenanalyse der Sensoren mit einer Anforderungsanalyse durchgeführt wird, um die erforderliche Funktionalität zu erreichen.

Peter M
quelle
"Aus meiner Sicht wird hier die Annahme getroffen, dass der Türzustand ein binärer Zustand ist, obwohl es sich tatsächlich um einen ternären Zustand handelt:" und "Sie aktivieren den Betrieb des Ofens, wenn die Tür geschlossen oder nicht geschlossen ist Es ist dir egal, wie offen die Tür ist. " sich widersprechen. Ich glaube auch nicht, dass es um Türen und Sensoren geht.
Sanchises
@Sanchises Die Aussagen sind nicht widersprüchlich, da Sie sie aus dem Zusammenhang gerissen haben. Die erste Aussage steht im Zusammenhang damit, dass zwei entgegengesetzte binäre Messungen eines ternären Zustands nicht durch binäre Negation ausgetauscht werden können. Die zweite Aussage steht im Zusammenhang damit, dass möglicherweise nur eine teilweise Kenntnis eines ternären Zustands ausreicht, damit eine Anwendung ordnungsgemäß funktioniert. Bei den Türen bezog sich die Frage des OP auf das Öffnen und Schließen von Türen. Ich möchte nur auf eine offensichtliche Annahme hinweisen, die Einfluss darauf haben kann, wie Daten behandelt werden.
Peter M
-1

Konkrete Stilregeln sind immer schlecht. Richtlinien sind gut, aber am Ende gibt es oft Fälle, in denen Sie Dinge klarer machen können, indem Sie etwas tun, das den Richtlinien nicht ganz entspricht.

Das heißt, es gibt eine Menge Hass für die leeren anderen "es verschwendet Zeilen" "zusätzliche Eingabe", die nur schlecht sind. Sie könnten das Argument vorbringen, die Klammern in dieselbe Zeile zu verschieben, wenn Sie wirklich den vertikalen Abstand benötigen, aber wenn dies ein Problem ist, sollten Sie Ihr {ohnehin nicht in eine separate Zeile setzen.

Wie an anderer Stelle erwähnt, ist der else-Block sehr nützlich, um zu zeigen, dass im anderen Fall explizit nichts geschehen soll. Nachdem ich viel funktionales Programmieren durchgeführt habe (wo sonst noch etwas erforderlich ist), habe ich gelernt, dass Sie beim Schreiben des if immer das andere berücksichtigen sollten, obwohl es, wie @OldCurmudgeon erwähnt, wirklich zwei verschiedene Anwendungsfälle gibt. Man sollte ein anderes haben, man sollte nicht. Leider ist es nicht immer auf einen Blick zu erkennen, geschweige denn mit einem Linter, weshalb die Dogmatik „immer einen anderen Block setzen“.

Auch hier sind die absoluten Regeln schlecht. Ein leeres wenn zu haben kann komisch sein, besonders wenn es die Art ist, wenn das kein anderes braucht, also schreibe das alles lieber als ein! oder ein == falsch ist schlecht. Trotzdem gibt es viele Fälle, in denen das Negative Sinn ergibt. Ein häufiges Beispiel wäre das Zwischenspeichern eines Werts:

static var cached = null

func getCached() {
    if !cached {
        cached = (some calculation, etc)
    }

    return cached
}

Wenn die tatsächliche (mentale / englische) Logik ein Negativ beinhaltet, sollte dies auch für die if-Anweisung gelten.

zacaj
quelle