Czy używanie ELSE jest złym programowaniem? [Zamknięte]


18

Często spotykam błędy, które zostały spowodowane przez użycie ELSEkonstruktu. Doskonałym przykładem jest coś w stylu:

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

Dla mnie to krzyczy problem bezpieczeństwa. Wiem, że PasswordCheck może być logiczną logiką, ale nie umieszczałbym na nim zabezpieczeń moich aplikacji. Co by się stało, gdyby to był ciąg znaków, int itp?

Zwykle staram się unikać używania ELSE, a zamiast tego wybieram dwie całkowicie oddzielne instrukcje IF, aby sprawdzić, czego oczekuję. Cokolwiek innego wtedy zostanie zignorowane LUB zostanie specjalnie potraktowane.

Z pewnością jest to lepszy sposób na zapobieganie błędom / problemom z bezpieczeństwem w aplikacji.

Jak to robicie?


3
Jaki jest dla ciebie problem bezpieczeństwa? Co oznacza „passwordCheck”? Czy było sprawdzenie hasła? Czy konieczne jest sprawdzenie hasła? Użytkownik zdał? Użytkownik nie wprowadził poprawnego hasła?
LennyProgrammers,

20
I know that passwordCheck is likely to be a boolean...Co masz na myśli? W dowolnym silnym języku. passwordCheckbędzie co ty chcesz go mieć.
Bobby,

31
Myślę, że złe praktyki wcięć prowadzą do większej liczby błędów niż przy użyciu elseinstrukcji ...
gablin,

15
To wydaje się dziwne. Po pierwsze, skarżysz się na możliwy typ zwrotu, który passwordCheck()może nie być logiczny (co może być uzasadnionym problemem), a następnie obwiniasz go else? Nie rozumiem, jakie problemy elsepowodują.
David Thornley,

8
mmm, myślę, że pytanie, czy użycie innego jest złym programowaniem, jest złym programowaniem
Muad'Dib,

Odpowiedzi:


90

elseBlok zawsze powinien składać się z co chcesz domyślne zachowanie się.

Nie musisz ich unikać, po prostu używaj ich odpowiednio.

W twoim przykładzie domyślnym stanem powinno być uniemożliwienie dostępu. Trochę refaktoryzacji pozostawia Ci:

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

tzn. jeśli sprawdzanie hasła działa, wpuść je, w przeciwnym razie zawsze jest ważny komunikat o błędzie.

Możesz oczywiście dodać dodatkowe kontrole do swojej logiki, używając else ifraczej niż całkowicie osobnegoif instrukcji.


2
@ dave.b w kontekście przykładu, myślę, że byłby to problem bezpieczeństwa, ale jeśli jest to wszędzie w bazie kodu, na którą patrzysz, jest to bardziej znak tego, kto napisał to, potrzebując nieco więcej trening :)
RYFN,

9
Czy ktoś może rozwinąć kwestię bezpieczeństwa? if (! coś) {do a} else {do ​​b} kontra if (coś) {do b} else {do ​​a} jest logicznie równoważne nie? Próbuję zrozumieć, na czym polega różnica pod względem bezpieczeństwa?
Chris

11
@Chris: Myślę, że OP używa słabo napisanego języka. Dlatego passwordCheckmoże być cokolwiek, Fe null, co czyniłoby passwordCheck == falsesię falsei by umożliwić użytkownikowi do logowania z powodu błędu wewnętrznego.
Bobby

1
@Chris, rozumiem, że domyślnym stanem było zezwolenie na dostęp, co niekoniecznie jest zalecane.
RYFN,

3
Tak, to bardzo zależy od języka. W języku C #, gdzie ifwymaga a bool, zmienne muszą być definitywnie przypisane, wszystkie ścieżki muszą zwracać wartość itp., Nie mogę wymyślić żadnego powodu kolejności ifi elsemiałby znaczenie oprócz czytelności. To znaczy if(a){b();}{c();}powinno być równoważne z if(!a){c();{b();}. Z drugiej strony w JavaScript musisz być świadomy, że passwordCheckmoże być undefineditp.
Tim Goodman,

57

Nie, nie ma w tym nic złego ELSE. ELSEnie jest nowy GOTO. W rzeczywistości, używając dwóch IFs zamiastELSE może prowadzić do kilku problemów.

Przykład pierwszy:

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

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

Widzisz kopiowanie-wklej? Po prostu czeka na dzień, w którym zmienisz jedno i zapomnisz o drugim.

Przykład drugi:

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

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

Jak widać, drugi IFzostanie również wykonany dla pierwszego elementu, ponieważ warunek już się zmienił. W rzeczywistych programach takie błędy są trudniejsze do wykrycia.


12
Zgadzam się z tym. Copy-wklejeniu ifoświadczenie i odwracanie jego logiczną wyraz właśnie w celu uniknięcia else- teraz to jest złe programowanie! Nie tylko kopiujesz kod ( złe programowanie), ale także spowalniasz wydajność (jeśli sprawdzenie jest naprawdę skomplikowane, teraz robisz to dwa razy - ba ... no wiesz, co mówią o przedwczesnych optymalizacjach obecnie ... niezbyt dobre programowanie!).
gablin

Szczerze mówiąc, jeśli sprawdzanie powinno odbywać się według własnej metody, aby zwrócić true / false
billy.bob

Dobre przykłady, nie zapominajmy o wydajności użycia 2, jeśli czeków, w porównaniu do jednego, jeśli w połączeniu z innym. Zakładam, że w większości języków użycie if / else będzie działało lepiej niż użycie dwóch instrukcji if z jednym, który nie używa warunku.
Chris

1
dave.b: jasne, ale mogłoby się zacząć proste i powoli rosnąć; Kompleksowa kontrola w moim przykładzie ma na celu uczynienie problemu bardziej oczywistym.
user281377,

3
Tak - i nie zapominaj, że warunki mogą mieć skutki uboczne w większości języków, a jeśli są funkcje w warunkach, których tak naprawdę nie można stwierdzić, patrząc.
David Thornley,

18

Zawsze jest ELSE. Jeśli napiszesz

if(foo)
  bar();

faktycznie piszesz

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

Wszystko, co umieścisz na ścieżce ELSE, spoczywa na tobie.


7
-1. Ta odpowiedź jest zbyt śmieszna. Nie twierdzę, że to nieprawda. Po prostu mija się z celem. To, co kompilacja generuje z twojego kodu, nie ma nic wspólnego z praktykami kodowania i

3
Pytanie brzmiało: „Czy używanie ELSE jest złe programowanie?”. Moja odpowiedź brzmi: możesz udawać, że jej nie ma, ale nie unikaj jej.
LennyProgrammers,

5
jaki język ? w Javie nie ma jeszcze kodu bajtowego: P
IAdapter

@ acidzombie24 - bardzo dobrą praktyką jest zastanawianie się, czym jest „inne” z dowolnego pytania. Czasami jest to po prostu - rób wszystko co innego, ale pokazuje, że o tym pomyślałeś
Martin Beckett

14

Osobiście staram się unikać elsejak najwięcej, ale to nie jest dla bezpieczeństwa problemu z .

Podczas odczytywania kodu zagnieżdżone instrukcje utrudniają przestrzeganie logiki, ponieważ należy pamiętać, który zestaw warunków tam doprowadzi. Z tego powodu jestem wielkim fanem wczesnego wyjścia:

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

letThemIn();

Dotyczy to również fori whilepętle, w których będę używał continueibreak kiedy unika poziom wcięcia.

Chris Lattner mówi to lepiej niż ja w standardach kodowania LLVM .


Zgadzam się. „else” tworzy mentalny „widelec”, a my ludzie jesteśmy stworzeniami sekwencyjnymi.
user187291,

Fyi, to się nazywa warunek ochronny
CaffGeek,

@Chad: ah dzięki, zawsze miło jest zyskać nazwę dla rzeczy :)
Matthieu M.

1
+1. To jedyna odpowiedź, jaką mogę znieść, która ma wynik> 1. *writes an answer*

Nie staram się unikać innych jako takich, ale myślę, że zgadzam się z tym, co piszesz. Chodzi o to, że testowana rzecz powinna być wyjątkiem, a nie normalnym przypadkiem ( stackoverflow.com/questions/114342/… ). W tym przypadku błąd uwierzytelnienia jest wyjątkiem i (tylko) powinien zostać przetestowany.
hlovdal,

5

Następnie po prostu je zastąp

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

21
Jak o If ((passwordCheck == true) == true)? :-)
Hippo

1
Cóż, to mój styl, żeby poprawić czytelność. Możesz napisać if (! Wartość), ale wolę if (wartość! = Prawda)
Ahmet Kakıcı

7
Nie poprawia to czytelności. Po prostu dodaje hałasu. Jeszcze gorzej są programiści, którzy piszą zagnieżdżone konstrukcje tylko dlatego, że boją się operatorów boolowskich.
ak2

3
Jeśli nazwa zmiennej ma charakter opisowy, nie ma powodu, dla którego: if (someBooleanValue == true) powinno być konieczne. Byłoby lepiej: if (validPassword) {...
Mark Freedman,

Właśnie zastąpiłem bloki kodu w pytaniu. Jeśli przeczytałeś mój pierwszy komentarz, powiedziałem, że wolę używać if (wartość! = Prawda) zamiast if (! Wartość). Mam nadzieję, że zobaczysz różnicę między if (wartość) a if (! Wartość). Miałem na myśli, że nie używam operatora uzupełniającego. W przeciwnym razie masz rację, prawda oznacza prawdę.
Ahmet Kakıcı,

3

Podobnie jak Matthieu M., wolę wcześniejsze wyjście niż głęboko zagnieżdżone inne bloki ... To dobrze ilustruje programowanie obronne (jeśli złe warunki, nie ma sensu kontynuować). Wiele osób się z nami nie zgadza, preferując wyjątkowy punkt wyjścia; nie o to chodzi w debacie (tak myślę).

Teraz z pewnością używam, elsegdy ma to sens, szczególnie w przypadku prostych, krótkich alternatyw. Jak powiedziano, powielanie testu jest stratą czasu (programisty i procesora), źródłem zamieszania, a później błędów (gdy jedno się zmienia, a nie drugie).
Czasami dodaję komentarz do elseczęści, przypominając o tym, co było warunkiem (szczególnie jeśliif część jest długa, np. W starszym kodzie) lub jaka jest alternatywa.

Zauważ, że niektórzy skrajni zwolennicy programowania funkcjonalnego proponują się go całkowicie pozbyć if, na rzecz dopasowania wzorców ... Trochę zbyt ekstremalnie jak na mój gust. :-)


1
na marginesie: jeśli masz konstrukcję if w czystym języku funkcjonalnym, naprawdę potrzebujesz innej. Każde wyrażenie musi coś zwrócić!
tokland

2

Nie ma nic złego w korzystaniu z ELSE. Może to jednak prowadzić do zbyt skomplikowanego kodu, który jest trudny do odczytania i zrozumienia. Może to oznaczać zły projekt. Z pewnością oznacza to dodatkowe przypadki użycia, które będą musiały zostać przetestowane.

Spróbuj usunąć ELSE, jeśli możesz - ale nie bądź na to paranoikiem. Steve McConnell nazywa ten kod linii prostej w Code Complete. Tj. Jest prosta prosta ścieżka przez twój kod.

Podejścia do wypróbowania konkretnego problemu:

  • stosować polimorfizm. Na granicy systemu sprawdź poprawność poświadczeń bezpieczeństwa użytkownika. Jeśli są uzasadnione, zwróć obiekt sesji - z dostępem do odpowiednich części systemu lub wyrzuć wyjątek. Może to jednak uczynić twój system bardziej złożonym. Więc decydujesz, co jest łatwiejsze do zrozumienia i utrzymania.

Ogólnie rzecz biorąc - poniższe elementy mogą pomóc w ograniczeniu ELSE w kodzie:

  • dobre wymagania mogą zmniejszyć potrzebę takich decyzji w kodzie. W niektórych przypadkach może nie być konieczne wdrożenie przypadku użycia.
  • wyraźniejszy projekt. Maksymalna spójność i minimalizacja sprzężenia. Dzięki temu komponenty nie duplikują decyzji podjętych w innych komponentach.
  • obsługa wyjątków w celu zarządzania przypadkami błędów.
  • polimorfizm (patrz przykład powyżej).
  • instrukcje switch - są to gloryfikowane ELSE, ale w niektórych sytuacjach są lepsze.

2
Dodałbym również „Przenieś złożone kontrole logiczne do oddzielnej funkcji”.
gablin

2

Twoje przypuszczenie, że kod jest wyciekiem zabezpieczeń, może być lub nie być prawdziwe w zależności od używanego języka . W kodzie C może to być problem (szczególnie dlatego, że w C boolean jest tylko liczbą całkowitą niezerową lub zerową) - ale w najbardziej typowych językach (tj. Sprawdzanie typu środowiska wykonawczego), jeśli passwordCheckzmienna została zadeklarowana jako boolean, nie ma sposobu, aby przypisać coś innego. W rzeczywistości wszystko w ifpredykacie musi zostać przetworzone na wartość logiczną, niezależnie od tego, czy używasz operatorów boolowskich, czy po prostu używasz wartości. Jeśli udało się powiązać inny typ obiektupasswordCheck środowiskiem wykonawczym, wygenerowałby jakiś rodzaj nielegalnego wyjątku rzutowania.

Proste konstrukcje if / else są znacznie łatwiejsze do odczytania niż konstrukcje if / if - i mniej podatne na nieumyślne problemy, jeśli ktoś spróbuje przerzucić konstrukt. Weźmy ten sam przykład przez sekundę:

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

if(passwordCheck) {
    letThemIn();
}

Znaczenie wzajemnie wykluczających się klauzul, które chcesz wykonać powyżej, zostało utracone. To właśnie przekazuje konstrukcja if / else. Dwie wzajemnie wykluczające się gałęzie egzekucji, przy czym jedna z nich zawsze będzie działać. Jest to ważna część bezpieczeństwa - upewnienie się, że nie ma sposobu, aby letThemInzadzwonić denyAccess.

W celu zapewnienia przejrzystości kodu oraz w celu zapewnienia największej ochrony sekcji krytycznych powinny one znajdować się w klauzuli podstawowej ( ifczęści). Domyślne zachowanie niezgodne powinno znajdować się w alternatywnej klauzuli ( elseczęści). Na przykład:

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

UWAGA: pracując z różnymi językami, opracowałem nawyk kodowania, który pomaga uniknąć pytania „co jeśli to ciąg znaków?” Zasadniczo należy umieścić stałą na pierwszym miejscu w wyrażeniu boolowskim. Na przykład zamiast sprawdzaniapasswordCheck == false sprawdzam false == passwordCheck. Pozwala to również uniknąć przypadkowego problemu przypisania możliwego w C ++. Przy takim podejściu kompilator będzie narzekał, jeśli piszę =zamiast ==. W językach takich jak Java i C # kompilator traktowałby przypisanie w klauzuli if jako błąd, ale C ++ chętnie je zaakceptuje. Dlatego też mam tendencję do sprawdzania wartości zerowej przy nullpierwszym.

Jeśli rutynowo zmieniasz języki, ustawianie stałej jako pierwszej jest bardzo pomocne. Jednak w moim zespole jest to sprzeczne ze standardem kodowania i kompilator i tak wychwytuje te problemy. Przełamanie może być trudne.


1

Mówiąc to za pomocą else podczas programowania jest złe, jest jak mówienie, że używanieotherwise podczas mówienia jest złe.

Oczywiście, oba mogą być używane w zły sposób, ale to nie znaczy, że należy ich unikać tylko dlatego, że popełniłeś błąd, który ich obejmował. Nie zdziwiłbym się, gdyby wiele błędów zależało od brakującej defaultsprawy w switchoświadczeniu.


1

Pomyśl o Elsebiałej liście przepływu aplikacji. Sprawdzasz warunki, które POWINNY umożliwić kontynuowanie przepływu aplikacji, a jeśli nie są spełnione, toElse wykonywane jest zadanie rozwiązania problemu, przerwania wykonywania aplikacji lub coś podobnego.

Else samo w sobie nie jest złe, ale jeśli używasz go słabo, możesz zobaczyć niepożądane efekty.

Również w odniesieniu do twojego oświadczenia o

„Wiem, że sprawdzanie hasła może być logiczne, ale nie umieszczałbym na nim zabezpieczeń moich aplikacji”.

W przypadku opracowywanych metod ZAWSZE zwracaj jeden typ danych. Chociaż PHP Core jest zaśmiecony kodem, który zwraca dwa lub więcej typów danych, jest to zła praktyka, ponieważ powoduje zgadywanie wywołań funkcji. Jeśli musisz zwrócić więcej niż jeden typ danych, zastanów się nad rzuceniem wyjątku (uważam, że często jest to powód, dla którego chciałbym zwrócić inny typ danych - coś poszło okropnie, okropnie źle) lub rozważyć zmianę struktury kodu, abyś mógł zwraca tylko jeden typ danych.


Nie wiedziałem, że istnieją języki, które zwracają więcej niż jeden typ danych! Masz na myśli funkcje polimorficzne? Wierzę, że ilekroć przeciążę funkcję, zawsze zwraca ten sam typ danych, chociaż może to wymagać różnych argumentów.
Michael K,

1
W luźno wpisanych językach, a zwłaszcza w PHP, funkcje mogą zwracać więcej niż jeden typ danych. np .: stristr- "Zwraca dopasowany podciąg. Jeśli igła nie zostanie znaleziona, zwraca FAŁSZ"
Craige

@Michael, funkcja PHP może zwrócić wszystko, co chcesz. Nie ma ograniczeń typu danych. Moja najbardziej skomplikowana funkcja zwraca true / false / null, ale nic nie stoi na przeszkodzie, abyś napisał funkcję, która zwraca true / integer / null / string / float / array.
TRiG,

Ciekawy. Nigdy nie pracowałem z PHP. Dziękuję za wyjaśnienie - prawdopodobnie pomóż mi w przyszłości! w pewnym sensie jak Javascript?
Michael K,

@Michael - Rzeczywiście całkiem podobny do Javascript.
Craige

1

Po pierwsze. LOL! Nie ma żadnego powodu, aby unikać w ogóle. NIE jest to zła praktyka w żaden sposób, w żadnym kształcie ani formie.

Jeśli cokolwiek, kod powinien być

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

Nie ma dwóch, jeśli tam jest i nie ma nic innego. Tak robię we wszystkich moich aplikacjach oprócz tej, w której zgłaszam wyjątek. W mojej funkcji jest wychwytywany wyjątek, który sprawdza adres URL pod kątem wyświetlenia właściwej strony (lub alternatywnie mogę umieścić funkcję catch / check w funkcji błędu asp.net). Drukuje ogólną stronę z informacją, że nie autoryzuj lub jakąkolwiek wiadomość, której używam w wyjątku (zawsze sprawdzam typ wyjątku i ustawiam kod stanu HTTP).

-Edytuj- jak pokazano w przykładzie amunicji dwa, jeśli jest absurdalny. Naprawdę inne jest tak samo dobre lub lepsze niż if. Jeśli w ogóle należy tego unikać (chociaż ja osobiście tego nie robię, ale często używam return i break), ponieważ powiedziano, że więcej ścieżek kodu zwiększa prawdopodobieństwo błędów. Widzieć złożoność cykliczną

-Edycja 2- Jeśli martwisz się, czy użycie / else. Zauważę również, że wolę umieścić najkrótszy blok kodu na górze, taki jak

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

Zamiast

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

0

Kiedy mogę, mogę ustawić wartość domyślną przed warunkami warunkowymi. Wydaje mi się, że jest to trochę łatwiejsze do odczytania i bardziej jednoznaczne, ale to tylko preferencja. Mam tendencję do unikania negatywnych warunków w moim kodzie. Nie jestem wielkim fanem sprawdzania! Foo lub false == foo i wydaje mi się, że inaczej jest rodzajem warunkowego odpowiednika negatywu.

foo = bar;

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

zamiast ...

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

Poprzedni blok kodu wydaje mi się trochę łatwiejszy do odczytania. Bardziej naturalna wydaje mi się sceptyczna paranoja dotycząca mojego kodu. Ustawienie wartości domyślnej niezależnie od warunków sprawia, że ​​czuję się komfortowo: P


0

Twierdziłbym, że należy w jak największym stopniu unikać wszelkiego rodzaju logiki rozgałęziania. Chociaż nie ma nic złego w przypadku ELSE lub IF, istnieje wiele sposobów pisania kodu, aby zminimalizować potrzebę użycia dowolnej logiki rozgałęziającej. Nie twierdzę, że logikę rozgałęziania można całkowicie wyeliminować - będzie potrzebna w niektórych miejscach - ale możliwe jest przefakturowanie kodu, aby wyeliminować sporą jego część. W większości przypadków poprawi to zrozumiałość i dokładność kodu.

Na przykład operatorzy trójskładnikowi są zwykle dobrymi kandydatami:

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

Stosując podejście trójskładnikowe:

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

Operatorzy trójskładnikowi dobrze przesuwają rozgałęzienie w prawo.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.