Verwendet ELSE schlechte Programmierung? [geschlossen]

18

Ich bin oft auf Fehler gestoßen, die durch die Verwendung des ELSEKonstrukts verursacht wurden. Ein Paradebeispiel ist etwa Folgendes:

If (passwordCheck() == false){
    displayMessage();
}else{
    letThemIn();
}

Für mich schreit dies nach Sicherheitsproblemen. Ich weiß, dass passwordCheck wahrscheinlich ein Boolescher Wert ist, aber ich würde die Sicherheit meiner Anwendungen nicht darauf platzieren. Was würde passieren, wenn es sich um einen String, Int usw. handelt?

Normalerweise versuche ich, die Verwendung zu vermeiden ELSE, und wähle stattdessen zwei vollständig separate IF-Anweisungen, um zu testen, was ich erwarte. Alles andere wird dann entweder ignoriert oder gezielt behandelt.

Dies ist sicherlich eine bessere Möglichkeit, um zu verhindern, dass Bugs / Sicherheitsprobleme in Ihre App gelangen.

Wie macht ihr das?

Billy Bob
quelle
3
Was ist das Sicherheitsproblem für Sie? Was bedeutet "passwordCheck"? Es wurde ein Passwort überprüft? Muss ein Passwort überprüft werden? Der Benutzer hat bestanden? Der Benutzer hat das richtige Passwort nicht eingegeben?
LennyProgrammers
20
I know that passwordCheck is likely to be a boolean...Was meinst du? In jeder stark typisierten Sprache. passwordCheckwird sein, was immer du willst.
Bobby
31
Ich denke, schlechte Einrückungspraktiken führen zu mehr Fehlern als die Verwendung von elseAnweisungen ...
gablin
15
Das scheint seltsam. Zuerst beschweren Sie sich über die mögliche Rückgabeart, dass Sie passwordCheck()möglicherweise nicht boolesch sind (was ein vernünftiges Anliegen sein könnte), und geben Sie dann die Schuld dafür else? Ich sehe nicht, welche Probleme die elseUrsachen haben.
David Thornley
8
mmm, ich denke, zu fragen, ob die Verwendung von sonst schlecht programmiert ist, ist schlecht programmiert
Muad'Dib

Antworten:

90

Der elseBlock sollte immer aus dem bestehen, was das Standardverhalten sein soll.

Sie müssen nicht umgangen werden. Achten Sie nur darauf, dass Sie sie ordnungsgemäß verwenden.

In Ihrem Beispiel sollte der Standardstatus sein, keinen Zugriff zuzulassen. Ein kleines Refactoring lässt Sie mit:

If (passwordCheck)
{
   letThemIn();
}
else
{
   displayMessage();
}

dh wenn die Passwortprüfung funktioniert, lassen Sie sie ein, andernfalls ist es immer gültig, eine Fehlermeldung anzuzeigen.

Sie können Ihrer Logik natürlich zusätzliche Prüfungen hinzufügen, indem Sie Anweisungen verwenden, die else ifnicht vollständig voneinander getrennt sind if.

RYFN
quelle
2
@ Dave.b im Kontext des Beispiels, ich denke, es wäre ein Sicherheitsproblem, aber wenn dies überall in der Codebasis ist, die Sie betrachten, ist es eher ein Zeichen dafür, wer es geschrieben hat, dass es ein bisschen mehr braucht Üben :)
RYFN
9
Kann jemand den Sicherheitsaspekt näher erläutern? if (! etwas) {do a} else {do ​​b} versus if (etwas) {do b} else {do ​​a} ist logisch äquivalent nein? Ich versuche zu verstehen, was der Unterschied in Bezug auf die Sicherheit ist.
Chris
11
@ Chris: Ich denke, das OP verwendet eine schwach typisierte Sprache. Daher passwordCheckkönnte alles, fe sein null, was machen würde passwordCheck == falsezu falseund würde der Benutzer Login erlaubt aufgrund eines internen Fehlers.
Bobby
1
@ Chris, mein Verständnis war, dass der Standardzustand das Zulassen des Zugriffs war, was nicht unbedingt ratsam ist.
RYFN
3
Ja, das ist sehr sprachabhängig. In C #, wo ifes ein erfordert bool, müssen Variablen definitiv zugewiesen werden, alle Pfade müssen einen Wert zurückgeben usw. Ich kann mir keinen Grund für die Reihenfolge vorstellen ifund elsewäre neben der Lesbarkeit von Bedeutung. Das heißt, if(a){b();}{c();}sollte gleichbedeutend sein mit if(!a){c();{b();}. In JavaScript müssen Sie sich andererseits bewusst sein, dass passwordCheckdies u undefined. Ä. Sein könnte .
Tim Goodman,
57

Nein, daran ist nichts auszusetzen ELSE. ELSEist nicht das neue GOTO. Tatsächlich kann die Verwendung von zwei IFstatt ELSEzu mehreren Problemen führen.

Beispiel eins:

if (this(is(a(very())==complex(check())))) {
   doSomething();
}

if (!this(is(a(very())==complex(check())))) {
   doTheOtherThing();
}

Sie sehen das Kopieren-Einfügen? Es wartet nur auf den Tag, an dem Sie das eine wechseln und das andere vergessen.

Beispiel zwei:

foreach(x in y) {
  if (first_time) {
    first_time = false;
    doSomething();
  }

  if (!first_time) {
    doTheOtherThing();
  }
}

Wie Sie sehen, wird die zweite IFauch für die erste Position ausgeführt, da sich die Bedingung bereits geändert hat. In realen Programmen sind solche Fehler schwerer zu erkennen.

user281377
quelle
12
Ich stimme dem zu. Kopieren Sie eine ifAnweisung, und invertieren Sie ihren booleschen Ausdruck, um eine else- nun , das ist schlechte Programmierung! Sie duplizieren nicht nur Code ( schlechte Programmierung), sondern verlangsamen auch die Leistung (wenn die Überprüfung wirklich kompliziert ist, tun Sie dies jetzt zweimal - na ja , Sie wissen, was über vorzeitige Optimierungen gesagt wird heutzutage ... nicht so gut programmiert!).
Gablin
Um ehrlich zu sein, sollte If check eine eigene Methode sein, um true / false zurückzugeben
billy.bob 20.12.10
Gute Beispiele, vergessen wir nicht die Leistung von 2, wenn Schecks gegen eine, wenn mit einem anderen gepaart. Ich würde in den meisten Sprachen davon ausgehen, dass die Verwendung eines if / else-Befehls eine bessere Leistung erbringt als die Verwendung von zwei if-Anweisungen mit einer nicht unter der Bedingung.
Chris
1
dave.b: sicher, aber es könnte einfach anfangen und langsam wachsen; Die komplexe Überprüfung in meinem Beispiel dient dazu, das Problem klarer zu machen.
user281377
3
Ja - und vergessen Sie nicht, dass Bedingungen in den meisten Sprachen Nebenwirkungen haben können, und ob es Funktionen in den Bedingungen gibt, können Sie wirklich nicht durch das Schauen erkennen.
David Thornley
18

Es gibt immer ein ELSE. Wenn du schreibst

if(foo)
  bar();

du schreibst tatsächlich

if(foo)
{
  bar();
}
else
{
   // do nothing
}

Was auch immer Sie in den ELSE-Pfad stellen, liegt in Ihrer Verantwortung.

LennyProgrammierer
quelle
7
-1. Diese Antwort ist zu lächerlich. Ich sage nicht, dass es nicht wahr ist. Es fehlt nur der Punkt. Was die Kompilierung aus Ihrem Code generiert, hat nichts mit den Codierungspraktiken und Fehlern zu tun, die Sie schreiben
3
Die Frage lautete "Verwendet ELSE schlechte Programmierung?". Meine Antwort lautet: Sie können so tun, als wäre es nicht da, aber Sie können es nicht vermeiden.
LennyProgrammers
5
welche Sprache ? in Java ist das übrige nicht im Bytecode: P
IAdapter
@ acidzombie24 - es ist eine sehr gute Praxis, zu überlegen, was das "Andere" von irgendeiner Frage ist. Manchmal ist es einfach - mache alles andere in der Funktion, aber es zeigt, dass du darüber nachgedacht hast
Martin Beckett
14

Ich persönlich neige dazu, elseso viel wie möglich zu vermeiden , aber es ist kein Sicherheitsproblem .

Beim Lesen von Code erschweren verschachtelte Anweisungen das Befolgen der Logik, da Sie sich merken müssen, welche Bedingungen dahin führen. Aus diesem Grund bin ich ein großer Fan des vorzeitigen Ausscheidens:

if (checkPassword != OK) { displayMessage(); return; }

letThemIn();

Dies gilt auch für forund whileSchleifen, in denen ich verwenden werde, continueund breakwann immer es eine Einrückungsebene vermeidet.

Chris Lattner sagt es besser als ich in den LLVM Coding Standards .

Matthieu M.
quelle
Genau. "else" schafft eine mentale "Gabelung" und wir Menschen sind sequenzielle Wesen.
User187291
Zu Ihrer Information, es wird Wachzustand genannt
CaffGeek
@Chad: Ah danke, immer schön, Namen für Dinge zu bekommen :)
Matthieu M.
1
+1. Dies ist die einzige Antwort, die ich ausstehen kann, die eine Punktzahl von> 1 hat. *writes an answer*
Ich versuche nicht, anderen als solchen auszuweichen, aber ich glaube, ich stimme dem zu, was Sie schreiben. Der Punkt ist, dass das, was Sie testen, die Ausnahme und nicht der Normalfall sein sollte ( stackoverflow.com/questions/114342/… ). Hier ist ein Authentifizierungsfehler die Ausnahme und sollte (nur) getestet werden.
hlovdal
5

Dann ersetzen Sie sie einfach,

If (passwordCheck == true)
{
     letThemIn();
}
else
{
     displayMessage();
}
Ahmet Kakıcı
quelle
21
Wie wäre es If ((passwordCheck == true) == true)? :-)
Hippo
1
Nun, es ist mein Stil, um die Lesbarkeit zu verbessern. Sie können schreiben, wenn (! Wert), aber ich bevorzuge, wenn (Wert! = True)
Ahmet Kakıcı
7
Die Lesbarkeit wird dadurch nicht verbessert. Es fügt nur Rauschen hinzu. Schlimmer noch sind Programmierer, die verschachtelte if-Konstrukte schreiben, nur weil sie Angst vor den Booleschen Operatoren haben.
Ak2
3
Wenn der Variablenname beschreibend ist, gibt es keinen Grund, warum: if (someBooleanValue == true) erforderlich sein sollte. Dies wäre besser: if (validPassword) {...
Mark Freedman
Nun, ich habe gerade die Codeblöcke innerhalb der Frage ersetzt. Wenn Sie meinen ersten Kommentar gelesen haben, sagte ich, dass ich lieber if (value! = True) als if (! Value) verwende. Ich hoffe, Sie können den Unterschied zwischen if (value) und if (! Value) erkennen. Ich meinte, ich benutze keinen Komplementoperator. Ansonsten hast du recht, wahr bedeutet wahr.
Ahmet Kakıcı
3

Wie Matthieu M. ziehe ich einen frühen Ausstieg tief verschachtelten anderen Blöcken vor ... Es veranschaulicht eine gut defensive Programmierung (bei schlechten Bedingungen macht es keinen Sinn, fortzufahren). Viele Leute werden mit uns nicht einverstanden sein und einen einzigartigen Ausstiegspunkt bevorzugen; Es ist nicht der Punkt der Debatte (glaube ich).

Jetzt nutze ich sicherlich, elsewenn es Sinn macht, vor allem für einfache, kurze Alternativen. Wie bereits erwähnt, ist das Duplizieren des Tests eine Zeitverschwendung (Programmierer und Prozessoren), eine Quelle der Verwirrung und später der Fehler (wenn einer geändert wird, nicht der andere).
Irgendwann füge ich einen Kommentar zum elseTeil hinzu und erinnere daran, was der Zustand war (insbesondere, wenn das ifTeil lang ist, z. B. im Legacy-Code) oder was die Alternative ist.

Beachten Sie, dass einige extreme Befürworter der funktionalen Programmierung vorschlagen, sich vollständig iffür den Pattern Matching zu entscheiden ... Ein bisschen zu extrem für meinen Geschmack. :-)

PhiLho
quelle
1
als Randnotiz: Wenn Sie ein if-Konstrukt in Ihrer reinen funktionalen Sprache haben, dann brauchen Sie wirklich ein anderes. Jeder Ausdruck muss etwas zurückgeben!
Tokland
2

Es ist nichts Falsches an der Verwendung von ELSE. Dies kann jedoch zu übermäßig komplexem Code führen, der schwer zu lesen und zu verstehen ist. Dies kann auf ein schlechtes Design hindeuten. Es weist auf zusätzliche Anwendungsfälle hin, die getestet werden müssen.

Versuchen Sie ELSEs zu entfernen, wenn Sie können - aber seien Sie nicht paranoid. Steve McConnell nennt diesen geraden Code in Code Complete. Dh es gibt einen einfachen klaren Weg durch Ihren Code.

Lösungsansätze für Ihr spezielles Problem:

  • verwende Polymorphismus. Überprüfen Sie an der Grenze zu Ihrem System die Sicherheitsdaten des Benutzers. Wenn sie legitim sind, geben Sie ein Sitzungsobjekt mit Zugriff auf die relevanten Teile des Systems zurück oder lösen eine Ausnahme aus. Dies könnte Ihr System jedoch komplexer machen. Sie entscheiden, was einfacher zu verstehen und zu warten ist.

Im Allgemeinen kann Folgendes dazu beitragen, ELSEs in Ihrem Code zu reduzieren:

  • Gute Anforderungen können die Notwendigkeit solcher Entscheidungen im Kodex verringern. Möglicherweise müssen Sie den Anwendungsfall (else) überhaupt nicht implementieren.
  • übersichtlicheres Design. Maximale Kohäsion und minimale Kopplung. Dies stellt sicher, dass Komponenten Entscheidungen, die in anderen Komponenten getroffen wurden, nicht duplizieren.
  • Ausnahmebehandlung zur Verwaltung von Fehlerfällen.
  • Polymorphismus (siehe Beispiel oben).
  • switch-Anweisungen - Dies sind verherrlichte ELSEs, sind aber in bestimmten Situationen besser.
Conor
quelle
2
Ich würde auch "Verschieben Sie komplexe boolesche Prüfungen in eine separate Funktion".
Gablin
2

Ihre Vermutung, dass der Code ein Sicherheitsleck ist, kann je nach verwendeter Sprache zutreffen oder auch nicht . In C-Code kann dies ein Problem sein (insbesondere, weil in C ein Boolescher Wert nur ein Int ist, der nicht Null oder Null ist). In den am häufigsten typisierten Sprachen (dh Laufzeit-Typprüfung) kann die passwordCheckVariable jedoch als Boolescher Wert deklariert werden. Es gibt keine Möglichkeit, etwas anderes zuzuweisen. Tatsächlich muss alles in einem ifPrädikat in einen Booleschen Wert aufgelöst werden, unabhängig davon, ob Sie die Booleschen Operatoren oder einfach den Wert verwenden. Wenn es Ihnen gelingt, einen anderen Objekttyp an passwordCheckdie Laufzeit zu binden, würde dies eine Art unzulässiger Ausnahmebedingung auslösen.

Einfache if / else-Konstrukte sind viel einfacher zu lesen als if / if-Konstrukte - und weniger anfällig für versehentliche Probleme, wenn jemand versucht, das Konstrukt umzudrehen. Nehmen wir das gleiche Beispiel für eine Sekunde:

if(passwordCheck == false) {
    denyAccess();
}

if(passwordCheck) {
    letThemIn();
}

Die Bedeutung der sich gegenseitig ausschließenden Klauseln, die Sie oben ausführen möchten, geht verloren. Das vermittelt das if / else-Konstrukt. Zwei sich gegenseitig ausschließende Ausführungszweige, von denen immer einer ausgeführt wird. Dies ist ein wichtiger Teil der Sicherheit - um sicherzustellen, dass letThemInnach Ihrem Anruf keine Möglichkeit mehr besteht denyAccess.

Aus Gründen der Klarheit des Codes und um sicherzustellen, dass kritische Abschnitte am besten geschützt sind, sollten sie in der Primärklausel (dem ifTeil) enthalten sein. Das standardmäßige nicht konforme Verhalten sollte in der alternativen Klausel (dem elseTeil) enthalten sein. Beispielsweise:

if(passwordCheck) {
    letThemIn();
} else {
    denyAccess();
}

HINWEIS: In der Arbeit mit verschiedenen Sprachen habe ich ein Codierungs-Habbit entwickelt, mit dem die Frage "Was ist, wenn es sich um eine Zeichenfolge handelt?" Im Wesentlichen muss die Konstante im booleschen Ausdruck an erster Stelle stehen. Zum Beispiel, anstatt zu überprüfen, überprüfe passwordCheck == falseich false == passwordCheck. Dies vermeidet auch das in C ++ mögliche versehentliche Zuweisungsproblem. Bei dieser Vorgehensweise beschwert sich der Compiler, wenn ich =statt tippe ==. In Sprachen wie Java und C # würde der Compiler die Zuweisung in der if-Klausel als Fehler behandeln, aber C ++ wird dies gerne akzeptieren. Aus diesem Grund neige ich auch dazu, Nullprüfungen beim nullErsten durchzuführen .

Wenn Sie routinemäßig die Sprache wechseln, ist es sehr hilfreich, die Konstante an die erste Stelle zu setzen. In meinem Team widerspricht dies jedoch dem Kodierungsstandard, und der Compiler erkennt diese Probleme trotzdem. Es kann schwierig sein, sich davon zu lösen.

Berin Loritsch
quelle
1

Zu sagen, dass das Verwenden elsebeim Programmieren schlecht ist, ist wie zu sagen, dass das Verwenden otherwisebeim Sprechen schlecht ist.

Sicher, sie können beide auf schlechte Weise verwendet werden, aber das bedeutet nicht, dass sie vermieden werden sollten, nur weil Sie einen Fehler gemacht haben, der sie zufällig eingeschlossen hat. Es würde mich nicht wundern, wenn viele Fehler von einem fehlenden defaultFall in einer switchAussage abhängen .

gablin
quelle
1

Stellen Sie sich vor, Sie Elselisten Ihren Bewerbungsfluss auf. Sie prüfen, ob Bedingungen erfüllt sind, die das Fortfahren des Anwendungsflusses SOLLTEN. Wenn diese Bedingungen nicht erfüllt sind, werden Sie Elseausgeführt, um das Problem zu beheben, die Anwendungsausführung abzubrechen oder ähnliches.

Else an sich ist nicht schlecht, aber wenn Sie es schlecht verwenden, können Sie unerwünschte Effekte sehen.

Auch in Bezug auf Ihre Aussage über

"Ich weiß, dass passwordCheck wahrscheinlich ein Boolescher Wert ist, aber ich würde die Sicherheit meiner Anwendungen nicht darauf platzieren."

Für von Ihnen entwickelte Methoden wird IMMER ein Datentyp zurückgegeben. Obwohl PHP Core mit Code übersät ist, der zwei oder mehr Datentypen zurückgibt, ist dies eine schlechte Praxis, da Funktionsaufrufe erraten werden. Wenn Sie mehr als einen Datentyp zurückgeben müssen, ziehen Sie in Betracht, eine Ausnahme auszulösen (ich finde, dies ist oft der Grund, warum ich einen anderen Datentyp zurückgeben möchte - etwas lief schrecklich, schrecklich falsch), oder überlegen Sie, Ihren Code so zu strukturieren, dass Sie es können gibt nur einen Datentyp zurück.

Craige
quelle
Ich wusste nicht, dass es Sprachen gibt, die mehr als einen Datentyp zurückgeben! Beziehen Sie sich auf polymorphe Funktionen? Ich glaube, wenn ich eine Funktion überlade, hat sie immer den gleichen Datentyp zurückgegeben, obwohl sie unterschiedliche Argumente annehmen kann.
Michael K
1
In lose geschriebenen Sprachen und insbesondere in PHP können Funktionen mehr als einen Datentyp zurückgeben. Beispiel: stristr- "Gibt den passenden Teilstring zurück. Wird needle nicht gefunden, wird FALSE zurückgegeben"
Craige
@Michael, Eine PHP-Funktion kann alles zurückgeben, was Sie wollen. Es gibt keine Einschränkung für den Datentyp. Meine komplizierteste Funktion gibt true / false / null zurück, aber nichts (außer gesunder Menschenverstand) hindert Sie daran, eine Funktion zu schreiben, die true / integer / null / string / float / array zurückgibt.
TRiG
Interessant. Ich habe noch nie mit PHP gearbeitet. Vielen Dank für die Erklärung - wahrscheinlich helfen Sie mir in Zukunft! ein bisschen wie Javascript dann?
Michael K
@Michael - Ähnlich wie Javascript.
Craige
1

Als allererstes. LOL! Theres KEIN GRUND, zum sonst überhaupt zu vermeiden. Es ist keine schlechte Praxis in irgendeiner Weise, Form oder Gestalt.

Wenn überhaupt, sollte der Code sein

if(!IsLoggedIn) { ShowBadLoginOrNoAccessPage(); return }

Theres keine zwei wenns dort und es hat kein anderes. Dies ist, was ich in allen meinen Apps mache, mit Ausnahme einer, in der ich eine Ausnahme auslöse. Die Ausnahme ist in meiner Funktion abgefangen, die die URL überprüft, damit die richtige Seite angezeigt wird (oder ich kann alternativ die catch / check in asp.net-Fehlerfunktion setzen). Es wird eine generische Seite gedruckt, die besagt, dass keine Autorisierung vorliegt oder welche Meldung ich in der Ausnahme verwende (ich überprüfe immer die Art der Ausnahme und setze den http-Statuscode).

-Edit- wie in ammoQ Beispiel zwei gezeigt, wenns lächerlich ist. Wirklich, sonst ist es genauso gut oder besser als wenn. Wenn irgendetwas zu vermeiden ist (obwohl ich es persönlich nicht tue. Aber ich benutze return und break viel), da gesagt wurde, dass mehr Codepfade die Wahrscheinlichkeit von Fehlern erhöhen. Siehe zyklomatische Komplexität

-Bearbeiten 2- Wenn Sie besorgt sind, ob / else Verwendung. Ich beachte auch, dass ich es vorziehen würde, den kürzesten Codeblock nach oben zu setzen, wie z

if(cond == false) {
    a()
    b()
    c()
    onetwothree()
}
else
{
    a()
    b()
    c()
    more()
    onetwothree()
    code()
    longer()
}

Eher, als

if(cond) 
{
    a()
    b()
    c()
    more()
    onetwothree()
    code()
    longer()
}
else
{
    a()
    b()
    c()
    onetwothree()
}

quelle
0

Ich setze gerne einen Standard vor Bedingungen, wenn ich kann. Ich denke, es ist ein bisschen einfacher zu lesen und etwas expliziter, aber das ist nur eine Präferenz. Ich neige dazu, negative Bedingungen in meinem Code zu vermeiden. Ich bin kein großer Fan davon, nach! Foo oder false == foo zu suchen, und ich habe das Gefühl, dass sonst irgendwie das bedingte Äquivalent eines Negativs ist.

foo = bar;

if ('fubar' == baz) {
    foo = baz;
}

anstatt ...

if ('fubar' == baz) {
    foo = baz;
} else {
    foo = bar;
}

Der vorherige Codeblock scheint mir nur ein bisschen leichter zu lesen zu sein. Es erscheint mir natürlicher, eine Art skeptische Paranoia in Bezug auf meinen Code zu haben. Durch das Festlegen einer Standardeinstellung unabhängig von einer Bedingung fühle ich mich wohl: P


quelle
0

Ich würde argumentieren, dass die Verwendung von Verzweigungslogik jeglicher Art so weit wie möglich vermieden werden sollte. Während mit ELSE oder IF nichts falsch ist, gibt es viele Möglichkeiten, Code zu schreiben, um die Notwendigkeit der Verwendung von Verzweigungslogik zu minimieren. Ich sage nicht, dass Verzweigungslogik vollständig beseitigt werden kann - es wird an einigen Stellen benötigt -, aber es ist möglich, Code umzugestalten, um einen guten Teil davon zu beseitigen. In den meisten Fällen wird dadurch die Verständlichkeit und Genauigkeit Ihres Codes verbessert.

Zum Beispiel sind ternäre Operatoren normalerweise auch gute Kandidaten:

If VIP Then 
  Discount = .25
Else
  Discount = 0
End If
Total = (1 - Discount) * Total

Verwenden Sie einen ternären Ansatz:

Discount = VIP ? .25 : 0
Total = (1 - Discount) * Total

Ternäre Operatoren verschieben die Verzweigung gut nach rechts.

Mario T. Lanza
quelle