Oto bardzo uproszczony przykład . To niekoniecznie jest pytanie specyficzne dla języka i proszę zignorować wiele innych sposobów pisania funkcji i zmian, które można w niej wprowadzić. . Kolor jest wyjątkowego rodzaju
string CanLeaveWithoutUmbrella()
{
if(sky.Color.Equals(Color.Blue))
{
return "Yes you can";
}
else
{
return "No you can't";
}
}
Wielu ludzi, których spotkałem, ReSharper, i ten facet (którego komentarz przypomniał mi, że chciałem o to zapytać od dłuższego czasu) zaleciłby przeformułowanie kodu w celu usunięcia else
bloku, pozostawiając to:
(Nie pamiętam, co powiedziała większość, mógłbym nie zapytać inaczej)
string CanLeaveWithoutUmbrella()
{
if(sky.Color.Equals(Color.Blue))
{
return "Yes you can";
}
return "No you can't";
}
Pytanie: Czy wprowadzono wzrost złożoności poprzez pominięcie else
bloku?
Mam wrażenie, że else
bardziej bezpośrednio określa intencje, stwierdzając, że kod w obu blokach jest bezpośrednio powiązany.
Dodatkowo uważam, że mogę zapobiec subtelnym błędom logicznym, szczególnie po późniejszych modyfikacjach kodu.
Weźmy tę odmianę mojego uproszczonego przykładu (ignorując fakt, że or
operator, ponieważ jest to celowo uproszczony przykład):
bool CanLeaveWithoutUmbrella()
{
if(sky.Color != Color.Blue)
{
return false;
}
return true;
}
Ktoś może teraz dodać nowy if
blok na podstawie warunku po pierwszym przykładzie bez natychmiastowego prawidłowego rozpoznania pierwszego warunku nałożenia ograniczenia na swój własny warunek.
Jeśli else
blok byłby obecny, ktokolwiek dodałby nowy warunek, byłby zmuszony przenieść zawartośćelse
bloku (i jeśli w jakiś sposób go nadpisuje, heurystyka pokaże, że kod jest nieosiągalny, czego nie robi w przypadku if
ograniczenia jednego) .
Oczywiście istnieją inne sposoby zdefiniowania konkretnego przykładu, z których wszystkie zapobiegają tej sytuacji, ale to tylko przykład.
Długość podanego przeze mnie przykładu może zniekształcać wizualny aspekt tego, więc załóżmy, że przestrzeń zajmowana przez nawiasy jest względnie nieistotna dla reszty metody.
Zapomniałem wspomnieć o przypadku, w którym zgadzam się z pominięciem bloku else, i kiedy używam if
bloku do zastosowania ograniczenia, które musi być logicznie spełnione dla całego następującego kodu, takiego jak kontrola zerowa (lub inne zabezpieczenia) .
źródło
else
klauzuli (według mojego gustu będzie jeszcze bardziej czytelny, pomijając niepotrzebne nawiasy. Myślę jednak, że przykład nie jest dobrze wybrany, ponieważ w powyższy przypadek napisałbymreturn sky.Color == Color.Blue
(bez if / else). Przykład z innym typem zwrotu niż bool prawdopodobnie by to wyjaśniłOdpowiedzi:
Moim zdaniem preferowany jest wyraźny blok else. Kiedy to widzę:
Wiem, że mam do czynienia z wzajemnie wykluczającymi się opcjami. Nie muszę czytać, co jest w blokach if, aby móc powiedzieć.
Kiedy to widzę:
Na pierwszy rzut oka wygląda na to, że zwraca wartość false i ma opcjonalny efekt uboczny, że niebo nie jest niebieskie.
Moim zdaniem struktura drugiego kodu nie odzwierciedla tego, co faktycznie robi kod. To źle. Zamiast tego wybierz pierwszą opcję, która odzwierciedla logiczną strukturę. Nie chcę sprawdzać logiki powrotu / zerwania / kontynuacji w każdym bloku, aby poznać przepływ logiczny.
Dlaczego ktoś wolałby drugą, jest dla mnie tajemnicą, mam nadzieję, że ktoś, kto zajmie przeciwne stanowisko, oświeci mnie swoją odpowiedzią.
Powiedziałbym, że warunki wartowników są w porządku, jeśli opuściłeś szczęśliwą ścieżkę. Wystąpił błąd lub awaria, która uniemożliwia kontynuowanie zwykłego wykonywania. Gdy tak się stanie, należy zgłosić wyjątek, zwrócić kod błędu lub cokolwiek odpowiedniego dla Twojego języka.
Wkrótce po oryginalnej wersji tej odpowiedzi w moim projekcie odkryto taki kod:
Nie wprowadzając zwrotu do innych elementów, przeoczono fakt, że brakowało zwrotu. Gdyby zastosowano inne, kod nawet by się nie skompilował. (Oczywiście o wiele bardziej martwi mnie to, że testy napisane na tym kodzie były dość złe, aby nie wykryć tego problemu).
źródło
else
bloku (może to być uciążliwe, gdy jestem zmuszony to zrobić, aby zachować zgodność z konwencjami dla bazy kodu). Ja też jestem zainteresowany zobaczeniem tutaj kontrapunktu.} // else
gdzie normalnie pójdzie jako kompromis między nimi.Głównym powodem usunięcia
else
bloku, który znalazłem, jest nadmierne wcięcie. Zwarcieelse
bloków umożliwia znacznie bardziej płaską strukturę kodu.Wiele
if
stwierdzeń jest w zasadzie strażnikami. Zapobiegają dereferencjowaniu wskaźników zerowych i innych „nie rób tego!” błędy. I prowadzą do szybkiego zakończenia bieżącej funkcji. Na przykład:Teraz może się wydawać, że
else
klauzula byłaby tutaj bardzo rozsądna:I rzeczywiście, jeśli to wszystko, co robisz, nie jest to dużo bardziej wcięte niż w powyższym przykładzie. Ale to rzadko wszystko, co robisz z prawdziwymi wielopoziomowymi strukturami danych (warunek ten występuje cały czas podczas przetwarzania popularnych formatów, takich jak JSON, HTML i XML). Jeśli więc chcesz zmodyfikować tekst wszystkich elementów potomnych danego węzła HTML, powiedz:
Strażnicy nie zwiększają wcięcia kodu. W przypadku
else
klauzuli cała praca zaczyna się przesuwać w prawo:To zaczyna mieć znaczący ruch w prawo, i jest to dość trywialny przykład, z tylko dwoma warunkami ochronnymi. Każdy dodatkowy strażnik dodaje kolejny poziom wcięcia. Prawdziwy kod Napisałem, że przetwarzam XML lub korzystam ze szczegółowych kanałów JSON, które mogą łatwo łączyć 3, 4 lub więcej warunków ochrony. Jeśli zawsze korzystasz z
else
, skończysz wcięciem 4, 5 lub 6 poziomów. Może więcej. A to zdecydowanie przyczynia się do poczucia złożoności kodu i więcej czasu poświęconego na zrozumienie, co z nim jest zgodne. Szybki, płaski styl wczesnego wyjścia eliminuje część tej „nadmiarowej struktury” i wydaje się prostszy.Dodatek Komentarze do tej odpowiedzi uświadomiły mi, że może nie być jasne, że NULL / puste obchodzenie się nie jest jedynym powodem warunku warunkowego. Nie blisko! Podczas gdy ten prosty przykład koncentruje się na obsłudze NULL / pustej i nie zawiera innych powodów, przeszukiwanie głębokich struktur, takich jak XML i AST, w celu znalezienia „odpowiednich” treści, na przykład, często obejmuje długą serię konkretnych testów w celu wyeliminowania nieistotnych węzłów i nieciekawych skrzynie Seria testów „jeśli ten węzeł nie jest istotny, zwróć” spowoduje taki sam dryf w prawo, jak NULL / puste osłony. W praktyce kolejne testy trafności są często łączone z wartościami NULL / pustymi zabezpieczeniami dla odpowiednio głębszych przeszukiwanych struktur danych - podwójny szum dla przesunięcia w prawo.
źródło
else
blok jest zbyteczny, gdy używamif
bloku do zastosowania ograniczenia, które musi być logicznie spełnione dla całego następującego kodu, takiego jak kontrola zerowa (lub inne zabezpieczenia).then
klauzuli (takie jak kolejne instrukcje zabezpieczające), to unikanie konkretnejelse
klauzuli nie ma szczególnej wartości . Rzeczywiście, zmniejszyłoby to klarowność i potencjalnie byłoby niebezpieczne (nie podając po prostu alternatywnej struktury, która jest / powinna istnieć).Kiedy to widzę:
Widzę wspólny idiom . Wspólny wzór , który w mojej głowie przekłada się na:
Ponieważ jest to bardzo powszechny idiom w programowaniu, programiści przyzwyczajeni do oglądania tego rodzaju kodu mają ukryte „tłumaczenie” w swojej głowie, ilekroć go zobaczą, co „konwertuje” je na właściwe znaczenie.
Nie potrzebuję tego wyraźnego
else
, moja głowa „umieszcza go tam” dla mnie, ponieważ rozpoznał wzór jako całość . Rozumiem znaczenie całego wspólnego wzorca, więc nie potrzebuję drobnych szczegółów, aby go wyjaśnić.Na przykład przeczytaj następujący tekst:
Czytałeś to?
Jeśli tak, to się mylisz. Przeczytaj tekst jeszcze raz. To, co faktycznie napisano, to
Są dwa tekście słowa „the”. Dlaczego więc tęskniłeś za jednym z dwóch?
To dlatego, że twój mózg widział wspólny wzór i natychmiast przetłumaczył go na znane znaczenie. Nie trzeba było czytać całego detalu po szczegółach, aby zrozumieć znaczenie, ponieważ rozpoznał już wzór po jego zobaczeniu. Dlatego zignorował dodatkowe „the ”.
To samo z powyższym idiomem. Programiści, którzy już czytają dużo kodu są przyzwyczajeni do tego wzorca , z
if(something) {return x;} return y;
. Nie potrzebują wyjaśnienia,else
aby zrozumieć jego znaczenie, ponieważ ich mózg „umieszcza je dla nich”. Rozpoznają go jako wzorzec o znanym znaczeniu: „co będzie po nawiasie zamykającym, będzie działało tylko jako domniemanieelse
poprzedniegoif
”.Moim zdaniem nie
else
jest to konieczne. Ale po prostu użyj tego, co wydaje się bardziej czytelne.źródło
Dodają zaśmiecenie wizualne w kodzie, więc tak, mogą zwiększyć złożoność.
Jednak jest też odwrotnie, nadmierne refaktoryzowanie w celu skrócenia długości kodu może również zwiększyć złożoność (oczywiście nie w tym prostym przypadku).
Zgadzam się ze stwierdzeniem, że
else
blok stwierdza zamiar. Ale mój wniosek jest inny, ponieważ dodajesz złożoność w kodzie, aby to osiągnąć.Nie zgadzam się z twoim stwierdzeniem, że pozwala to uniknąć subtelnych błędów w logice.
Zobaczmy przykład, teraz powinien on zwrócić prawdę tylko wtedy, gdy niebo jest niebieskie i jest wysoka wilgotność, wilgotność nie jest wysoka lub jeśli niebo jest czerwone. Ale wtedy pomylisz jedną nawias klamrowy i umieszczasz go w niewłaściwym miejscu
else
:Widzieliśmy wszystkie tego rodzaju głupie błędy w kodzie produkcyjnym i mogą być bardzo trudne do wykrycia.
Sugerowałbym następujące:
Uważam to za łatwiejsze do odczytania, ponieważ na pierwszy rzut oka widzę, że jest to ustawienie domyślne
false
.Również pomysł wymuszenia przesunięcia zawartości bloku w celu wstawienia nowej klauzuli jest podatny na błędy. Jest to w jakiś sposób związane z zasadą otwarcia / zamknięcia (kod powinien być otwarty dla rozszerzenia, ale zamknięty dla modyfikacji). Zmuszasz do modyfikacji istniejącego kodu (np. Koder może wprowadzić błąd w równoważeniu nawiasów klamrowych).
Wreszcie, doprowadzasz ludzi do odpowiedzi w określony sposób, który Twoim zdaniem jest właściwy. Przykładowo prezentujesz bardzo prosty przykład, a następnie stwierdzasz, że ludzie nie powinni go brać pod uwagę. Jednak prostota tego przykładu wpływa na całe pytanie:
Jeśli sprawa jest tak prosta jak prezentowana, moją odpowiedzią byłoby pominięcie jakiejkolwiek
if else
klauzul.return (sky.Color == Color.Blue);
Jeśli sprawa jest nieco bardziej skomplikowana, z różnymi klauzulami, odpowiedziałbym:
bool CanLeaveWithoutUmbrella () {
}
Jeśli sprawa jest skomplikowana i nie wszystkie klauzule zwracają wartość true (może zwracają wartość double), a ja chcę wprowadzić polecenie przerwania lub loggin, rozważę coś takiego:
}
Jeśli nie mówimy o metodzie, która coś zwraca, ale zamiast tego bloku if-else, który ustawia zmienną lub wywołuje metodę, to tak, dołączę ostatnią
else
klauzulę.Dlatego też należy wziąć pod uwagę prostotę przykładu.
źródło
Chociaż opisany przypadek if-else ma większą złożoność, w większości (ale nie wszystkich) praktycznych sytuacjach nie ma to większego znaczenia.
Wiele lat temu, kiedy pracowałem nad oprogramowaniem używanym w przemyśle lotniczym (musiałem przejść proces certyfikacji), pojawiło się twoje pytanie. Okazało się, że rachunek „else” wiązał się z kosztami finansowymi, ponieważ zwiększał złożoność kodu.
Dlatego.
Obecność „else” stworzyła niejawny trzeci przypadek, który musiał zostać oceniony - ani „ani”, ani „else”. Może myślisz (tak jak wtedy) WTF?
Wyjaśnienie przebiegło w ten sposób ...
Z logicznego punktu widzenia istnieją tylko dwie opcje - „if” i „else”. Certyfikowaliśmy jednak plik obiektowy generowany przez kompilator. Dlatego, gdy istniało „inne”, trzeba było przeprowadzić analizę, aby potwierdzić, że wygenerowany kod rozgałęzienia był poprawny i że nie pojawiła się tajemnicza trzecia ścieżka.
Porównaj to z przypadkiem, w którym istnieje tylko „jeśli”, a nie „inne”. W takim przypadku istnieją tylko dwie opcje - ścieżka „jeśli” i ścieżka „nie-jeśli”. Dwa przypadki do oceny są mniej skomplikowane niż trzy.
Mam nadzieję że to pomoże.
źródło
Najważniejszym celem kodu jest bycie zrozumiałym jako oddany (w przeciwieństwie do łatwego refaktoryzacji, co jest przydatne, ale mniej ważne). Aby to zilustrować, można użyć nieco bardziej złożonego przykładu w języku Python:
Jest to całkiem jasne - jest to odpowiednik
case
wyszukiwania instrukcji lub słownika, wersja wyższego poziomureturn foo == bar
:Jest to wyraźniejsze, ponieważ chociaż liczba tokenów jest wyższa (32 vs 24 dla oryginalnego kodu z dwiema parami klucz / wartość), semantyka funkcji jest teraz wyraźna: To tylko wyszukiwanie.
(Ma to konsekwencje dla refaktoryzacji - jeśli chcesz mieć efekt uboczny, jeśli
key == NIK
masz trzy możliwości:if
/elif
/else
i wstaw pojedynczą linię dokey == NIK
skrzynki.if
instrukcję dovalue
specjalnego przypadku przed zwróceniem.if
oświadczenie w dzwoniącym.Teraz widzimy, dlaczego funkcja wyszukiwania jest potężnym uproszczeniem: sprawia, że trzecia opcja jest oczywiście najprostszym rozwiązaniem, ponieważ oprócz powodowania mniejszej różnicy niż pierwsza opcja , jest jedyną, która zapewnia, że
value
robi tylko jedną rzecz. )Wracając do kodu OP, myślę, że może to służyć jako przewodnik dla złożoności
else
instrukcji: kod z wyraźnąelse
instrukcją jest obiektywnie bardziej złożony, ale ważniejsze jest to, czy sprawia, że semantyka kodu jest jasna i prosta. Na to pytanie należy odpowiedzieć indywidualnie, ponieważ każdy fragment kodu ma inne znaczenie.źródło
case
lubswitch
stwierdzenia) są mniej jednorodne. Kilka opcji to proste zwroty wartości, ale wtedy jedna lub dwie sprawy wymagają dodatkowego przetwarzania. A kiedy odkryjesz, że strategia wyszukiwania nie pasuje, musisz przepisać w zupełnie innejif
elif
...else
składni.Myślę, że to zależy od tego, jakiego rodzaju
if
oświadczenia używasz. Niektóreif
instrukcje mogą być traktowane jako wyrażenia, a inne mogą być postrzegane wyłącznie jako instrukcje kontroli przepływu.Twój przykład wygląda dla mnie jak wyrażenie. W językach podobnych do C operator trójskładnikowy
?:
jest bardzo podobny doif
wyrażenia, ale jest wyrażeniem, a drugiej części nie można pominąć. Ma to sens, że gałęzi else nie można pominąć w wyrażeniu, ponieważ wyrażenie musi zawsze mieć wartość.Za pomocą operatora potrójnego twój przykład wyglądałby tak:
Ponieważ jest to wyrażenie boolowskie, naprawdę należy je uprościć do samego końca
Zawarcie wyraźnej klauzuli else uczyni twoje zamiary wyraźniejszymi. Strażnicy mogą mieć sens w niektórych sytuacjach, ale przy wybieraniu między dwiema możliwymi wartościami, z których żadna nie jest wyjątkowa ani niezwykła, nie pomagają.
źródło
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.)
Kolejną rzeczą do przemyślenia w tym argumencie są nawyki, które promuje każde podejście.
if / else zmienia próbkę kodowania w kategoriach gałęzi. Jak mówisz, czasami ta wyraźność jest dobra. Istnieją naprawdę dwie możliwe ścieżki wykonania i chcemy to podkreślić.
W innych przypadkach istnieje tylko jedna ścieżka wykonania, a następnie wszystkie te wyjątkowe rzeczy, o które programiści muszą się martwić. Jak wspomniałeś, klauzule ochronne są do tego świetne.
Oczywiście jest 3 lub więcej przypadków (n), w których zwykle zachęcamy do porzucenia łańcucha if / else i użycia instrukcji case / switch lub polimorfizmu.
Główną zasadą, o której tu pamiętam, jest to, że metoda lub funkcja powinna mieć tylko jeden cel. Każdy kod z instrukcją if / else lub switch służy wyłącznie do rozgałęzienia. Powinien więc być absurdalnie krótki (4 linie) i delegować tylko do właściwej metody lub dawać oczywisty wynik (jak twój przykład). Kiedy twój kod jest tak krótki, trudno przeoczyć te wielokrotne zwroty :) Podobnie jak „goto uważany za szkodliwy”, każde oświadczenie rozgałęziające może być nadużywane, więc warto poświęcić trochę krytycznej przemyślenia na inne stwierdzenia. Jak wspomniałeś, sam blok else zapewnia miejsce do zlepiania się kodu. Ty i twój zespół będziecie musieli zdecydować, co się z tym czuje.
To, na co tak naprawdę narzeka to narzędzie, to fakt, że masz zwrot / przerwę / rzut w bloku if. Jeśli o to chodzi, napisałeś klauzulę ochronną, ale spieprzyłeś ją. Możesz uszczęśliwić narzędzie lub „zabawić” jego sugestię bez jej akceptowania.
źródło
else
semantycznie, narzędzia do analizy kodu zwykle szukają obcych instrukcji, ponieważ często uwidaczniają nieporozumienie dotyczące kontroli.else
Po dokonaniuif
że powraca marnuje bitów.if(1 == 1)
często wyzwala ten sam rodzaj ostrzeżenia.Myślę, że odpowiedź brzmi: to zależy. Twój przykładowy kod (jak pośrednio wskazujesz) po prostu zwraca ocenę warunku i najlepiej jest go przedstawić, jak wiadomo, jako pojedyncze wyrażenie.
Aby ustalić, czy dodanie warunku else wyjaśnia lub ukrywa kod, musisz ustalić, co reprezentuje IF / ELSE. Czy jest to (jak w twoim przykładzie) wyrażenie? Jeśli tak, to wyrażenie należy wyodrębnić i użyć. Czy to warunek ochronny? Jeśli tak, to ELSE jest niepotrzebne i wprowadza w błąd, ponieważ sprawia, że obie gałęzie wydają się równoważne. Czy to przykład polimorfizmu proceduralnego? Wyodrębnij to. Czy to stwarza warunki wstępne? To może być niezbędne.
Zanim zdecydujesz się na włączenie lub wyeliminowanie ELSE, zdecyduj, co reprezentuje, co powie ci, czy powinien on być obecny, czy nie.
źródło
Odpowiedź jest nieco subiektywna.
else
Blok może pomóc czytelności poprzez ustalenie, że jeden blok wykonaniem kodu wyłącznie do innego bloku kodu, bez konieczności przeczytać obu bloków. Może to być pomocne, jeśli, powiedzmy, oba bloki są stosunkowo długie. Są przypadki, w których posiadanieif
s bezelse
s może być bardziej czytelne. Porównaj następujący kod z wielomaif/else
blokami:z:
Drugi blok kodu zmienia zagnieżdżone
if
instrukcje w klauzule zabezpieczające. Zmniejsza to wcięcia i sprawia, że niektóre warunki powrotu są wyraźniejsze („jeślia != b
, to wrócimy0
!”)W komentarzach wskazano, że w moim przykładzie mogą być pewne dziury, więc jeszcze trochę go dopracuję.
Mogliśmy napisać tutaj pierwszy blok kodu, aby był bardziej czytelny:
Ten kod jest równoważny obu powyższym blokom, ale przedstawia pewne wyzwania.
else
Klauzula tutaj łączy te if razem. W powyższej wersji klauzuli ochronnej klauzule ochronne są od siebie niezależne. Dodanie nowego oznacza po prostu napisanie nowegoif
między ostatniąif
a resztą logiki. Można to również zrobić przy niewielkim obciążeniu poznawczym. Różnica polega jednak na tym, że przed tą nową klauzulą ochronną musi wystąpić pewna dodatkowa logika. Gdy oświadczenia były niezależne, moglibyśmy:Z połączonym
if/else
łańcuchem nie jest to takie łatwe. W rzeczywistości najłatwiejszą drogą jest zerwanie łańcucha, aby bardziej przypominać wersję klauzuli ochronnej.Ale to naprawdę ma sens. Używanie
if/else
słabo implikuje związek między częściami. Możemy przypuszczać, żea != b
jest to w jakiś sposób związanec > d
zif/else
wersją łańcuchową. Takie przypuszczenie byłoby mylące, ponieważ z wersji klauzuli wartowniczej wynika, że w rzeczywistości nie są one powiązane. Oznacza to, że możemy zrobić więcej dzięki niezależnym klauzulom, takim jak zmiana ich kolejności (być może w celu optymalizacji wydajności zapytań lub usprawnienia logicznego przepływu). Możemy też je poznawczo zignorować, gdy miniemy ich. Wif/else
łańcuchu nie jestem pewien, czy relacja równoważności pomiędzya
ib
nie pojawi się później.Związek sugerowany przez
else
jest również widoczny w głęboko zagnieżdżonym przykładowym kodzie, ale w inny sposób. Teelse
instrukcje są oddzielone od uzależnione są przyłączone, i są one podobne w odwrotnej kolejności do warunkowych (pierwszyelse
jest dołączony do ostatniegoif
ostatnielse
jest przymocowany do pierwszegoif
). Powoduje to dysonans poznawczy, w którym musimy cofać się przez kod, próbując wyrównać wcięcia w naszych mózgach. To trochę tak, jakby próbować dopasować kilka nawiasów. Jest jeszcze gorzej, jeśli wcięcie jest błędne. Opcjonalne nawiasy klamrowe też nie pomagają.To niezła koncesja, ale tak naprawdę nie unieważnia żadnej odpowiedzi. Mogę powiedzieć, że w twoim przykładzie kodu:
poprawną interpretacją takiego kodu może być to, że „musimy wyjść z parasolką, jeśli niebo nie jest niebieskie”. Istnieje ograniczenie polegające na tym, że niebo musi być niebieskie, aby poniższy kod działał poprawnie. Spełniłem definicję twojej koncesji.
Co jeśli powiem, że jeśli kolor nieba jest czarny, to jest czysta noc, więc parasol nie jest konieczny. (Istnieją prostsze sposoby na napisanie tego, ale są prostsze sposoby na napisanie całego przykładu).
Tak jak w większym przykładzie powyżej, mogę po prostu dodać nową klauzulę bez potrzeby. W
if/else
, nie może być więc pewien, że nie będzie bałagan coś w górę, zwłaszcza jeśli logika jest bardziej skomplikowana.Zwrócę też uwagę, że twój prosty przykład również nie jest taki prosty. Test na wartość niebinarną nie jest taki sam jak odwrotność tego testu z odwróconymi wynikami.
źródło
else
bloku jest niezbędne dla czytelności.Za bardzo uprościłeś swój przykład. Każda alternatywa może być bardziej czytelna, w zależności od większej sytuacji: w szczególności zależy to od tego, czy jedna z dwóch gałęzi stanu jest nienormalna . Pokażę ci: w przypadku, gdy jedna z gałęzi jest równie prawdopodobna,
... jest bardziej czytelny niż ...
... ponieważ pierwszy ma równoległą strukturę, a drugi nie. Ale kiedy większość funkcji jest poświęcona jednemu zadaniu i musisz najpierw sprawdzić niektóre warunki wstępne, ...
}
... jest bardziej czytelny niż ...
... ponieważ celowe nie konfigurowanie równoległej struktury stanowi wskazówkę dla przyszłego czytelnika, że uzyskiwanie danych wejściowych „naprawdę typu drugiego” tutaj jest nienormalne . (W prawdziwym życiu
ProcessInputDefinitelyOfTypeOne
nie byłaby to osobna funkcja, więc różnica byłaby jeszcze bardziej wyraźna.)źródło
if
-> Wykonaj wyjątek i pozostaw« jako strategię wczesnego powrotu . Pomyśl o kodzie, w którym domyślnie jest więcej sprawdzeń, szybciej byłoby zająć się przypadkiem wyjątkowym.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)
. Logicznie, każda pracaProcessInputOfTypeOne
zależy od tego, że!InputIsReallyTypeTwo(input)
musimy ją wychwycić poza naszym normalnym przetwarzaniem. NormalnieProcessInputOfTypeTwo(input);
byłoby,throw ICan'tProccessThatException
co uczyniłoby to podobieństwo bardziej oczywistym.u+
” zwykle wprowadza token „zakresu Unicode”, ale jeśli następny znak nie jest cyfrą szesnastkową, następnie należy wykonać kopię zapasową i przetworzyć „u” jako identyfikator. Tak więc główna pętla skanera wysyła polecenie „zeskanować token zakresu Unicode” nieco nieprecyzyjnie i zaczyna się od „... jeśli jesteśmy w tym niezwykłym szczególnym przypadku, wykonaj kopię zapasową, a następnie wywołaj podprogram skanowania-identyfikatora”.ProcessInputOfTypeOne
używa nieprecyzyjnego, ale szybkiego sprawdzania, które czasami zamiast tego łapie typ dwa.Tak, występuje wzrost złożoności, ponieważ klauzula else jest zbędna . Dlatego lepiej pomijać kod, aby zachować jego oszczędność i wredność. Działa tak samo, jak pomijanie nadmiarowych
this
kwalifikatorów (Java, C ++, C #) i pomijanie nawiasów wreturn
instrukcjach i tak dalej.Dobry programista myśli „co mogę dodać?” podczas gdy świetny programista myśli „co mogę usunąć?”.
źródło
else
bloku, jeśli jest to wyjaśnione w jednej linijce (co nie jest często), często ma tego rodzaju rozumowanie, więc widzę +1 za przedstawienie „domyślnego” argumentu, ale nie okaże się, że jest to wystarczająco mocne uzasadnienie.A good programmer things "what can I add?" while a great programmer things "what can I remove?".
Wydaje się, że jest to popularne powiedzenie, że wiele osób przesadza bez dokładnego rozważenia, a ja osobiście uważam, że jest to jeden z takich przypadków.Zauważyłem, zmieniając zdanie w tej sprawie.
Kiedy zadałem to pytanie rok temu, odpowiedziałbym na coś takiego:
»Powinien być tylko jeden
entry
exit
metodzie i jeden «. Mając to na uwadze, sugerowałbym przefakturowanie kodu w celu:Z punktu widzenia komunikacji jasne jest , co należy zrobić.
If
coś jest w tym przypadku, manipuluj wynikiem w jeden sposób ,else
manipuluj nim w inny sposób .Ale wiąże się to z niepotrzebnym
else
.else
Wyraża default-przypadek . Tak więc w drugim kroku mógłbym to zmienićNastępnie pojawia się pytanie: po co w ogóle używać zmiennej tymczasowej? Kolejny etap refaktoryzacji prowadzi do:
To jasne w tym, co robi. Idę nawet o krok dalej:
Lub dla osób o słabych nerwach :
Można go odczytać tak: »CanLeaveWithoutUmbrella zwraca bool . Jeśli nic specjalnego się nie wydarzy, zwraca false «To jest pierwsze czytanie, od góry do dołu.
A potem „analizujesz” warunek »Jeśli warunek jest spełniony, zwraca wartość true « Możesz całkowicie pominąć warunek i zrozumieć, co ta funkcja robi w domyślnych przypadku. Twoje oko i umysł muszą jedynie przeglądać niektóre litery po lewej stronie, bez czytania części po prawej stronie.
Tak to prawda. Ale ta informacja jest zbędna . Masz domyślny przypadek i jeden „wyjątek”, który jest objęty
if
-statement.Kiedy mam do czynienia ze skomplikowanymi strukturami, wybrałbym inną strategię, pomijając w
if
ogóle brzydkiego brataswitch
.źródło
So this is clear in what it does...
i ją rozwinąć? (Wcześniejsze przypadki mają również wątpliwe znaczenie). Mówię to, ponieważ to pytanie zadaje nam pytanie. Podałeś swoją postawę, pomijając blok „else”, i faktycznie jest to pozycja, która wymaga więcej wyjaśnień (i tej, która skłoniła mnie do zadania tego pytania). Z pytania: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.)
Krótko mówiąc, w tym przypadku pozbywanie się
else
słowa kluczowego jest dobrą rzeczą. Jest tutaj całkowicie redundantny i w zależności od tego, w jaki sposób sformatujesz kod, doda od jednego do trzech zupełnie bezużytecznych wierszy. Zastanów się, co jest wif
bloku:Dlatego jeśli
if
blok miałby zostać wprowadzony, cała reszta funkcji z definicji nie jest wykonywana. Ale jeśli nie zostanie wprowadzone, ponieważ nie ma dalszychif
instrukcji, cała reszta funkcji zostanie wykonana. Byłoby zupełnie inaczej, gdybyreturn
instrukcja nie znajdowała się wif
bloku, w którym to przypadku obecność lub brakelse
bloku miałby wpływ, ale tutaj nie miałby wpływu.W swoim pytaniu, po odwróceniu
if
stanu i pominięciuelse
, stwierdziłeś:Muszą wtedy przeczytać kod nieco dokładniej. Używamy języków wysokiego poziomu i przejrzystej organizacji kodu, aby złagodzić te problemy, ale dodanie całkowicie redundantnego
else
bloku dodaje do kodu „szum” i „bałagan” i nie powinniśmy również musieć odwracać ani przywracać naszychif
warunków. Jeśli nie potrafią odróżnić, nie wiedzą, co robią. Jeśli potrafią rozpoznać różnicę, zawsze mogą sami odwrócić pierwotneif
warunki.Tak się jednak zasadniczo dzieje. Wracasz z
if
bloku, więcif
ocena warunku dofalse
jest ograniczeniem, które musi być logicznie spełnione dla całego następującego kodu.Nie, będzie to oznaczać zmniejszenie złożoności kodu, a nawet zmniejszenie skompilowanego kodu, w zależności od tego, czy zostaną przeprowadzone pewne supertrywialne optymalizacje.
źródło
W konkretnym podanym przykładzie tak jest.
Myślę, że nie może być prostsze, że:
źródło
if
stwierdzenia. W rzeczywistości wyraźnie odradzam dalsze uproszczenie, używając dokładnie takiego kodu, jakiego użyłeś. Dodatkowoelse
blok nie zwiększa złożoności cyklicznej .Zawsze staram się pisać mój kod, aby intencja była jak najbardziej jasna. W twoim przykładzie brakuje kontekstu, więc zmodyfikuję go trochę:
Wydaje się, że naszą intencją jest to, że możemy wyjść bez parasola, gdy niebo jest niebieskie. Jednak ta prosta intencja zaginęła w morzu kodu. Istnieje kilka sposobów, dzięki którym możemy łatwiej to zrozumieć.
Po pierwsze, masz pytanie dotyczące
else
oddziału. Tak czy inaczej, nie mam nic przeciwko, ale zwykle pomijamelse
. Rozumiem argument o byciu jawnym, ale moim zdaniemif
stwierdzenia powinny zawierać „opcjonalne” gałęzie, takie jak tareturn true
. Nie nazwałbym tegoreturn false
oddziału „opcjonalnym”, ponieważ działa on jako nasz domyślny. Tak czy inaczej, uważam to za bardzo drobny problem i o wiele mniej ważny niż następujące:O wiele ważniejsze jest to, że twoje oddziały robią za dużo! Gałęzie, jak wszystko, powinny być „tak proste, jak to możliwe, ale nie więcej”. W tym przypadku użyłeś
if
instrukcji, która rozgałęzia się między blokami kodu (kolekcjami instrukcji), kiedy wszystko, czego naprawdę potrzebujesz, to wyrażenia (wartości). Jest to jasne, ponieważ a) bloki kodu zawierają tylko pojedyncze instrukcje oraz b) są to te same instrukcje (return
). Możemy użyć operatora trójskładnikowego,?:
aby zawęzić gałąź do innej części:Teraz dochodzimy do oczywistej redundancji, z którą wynik jest identyczny
this.color == Color.Blue
. Wiem, że twoje pytanie prosi nas o zignorowanie tego, ale myślę, że naprawdę ważne jest, aby zwrócić uwagę, że jest to bardzo proceduralny sposób myślenia pierwszego rzędu: rozkładasz wartości wejściowe i konstruujesz niektóre wartości wyjściowe. Zgadza się, ale jeśli to możliwe, znacznie silniej jest myśleć w kategoriach relacji lub transformacji między wejściem a wyjściem. W tym przypadku związek jest oczywiście taki sam, ale często widzę taki zawiły kod proceduralny, który przesłania pewne proste relacje. Przejdźmy do uproszczenia:Następną rzeczą, na którą zwracam uwagę, jest to, że twój interfejs API zawiera podwójnie negatywne: możliwe,
CanLeaveWithoutUmbrella
że takfalse
. To oczywiście upraszczaNeedUmbrella
bycietrue
, więc zróbmy to:Teraz możemy zacząć myśleć o twoim stylu kodowania. Wygląda na to, że używasz języka zorientowanego obiektowo, ale używając
if
instrukcji do rozgałęziania się między blokami kodu, skończyło się na pisaniu procedur kodu .W kodzie zorientowanym obiektowo wszystkie bloki kodu są metodami, a wszystkie rozgałęzienia są wykonywane za pomocą dynamicznej wysyłki , np. za pomocą klas lub prototypów. Można się spierać, czy to upraszcza sprawę, ale powinno to uczynić kod bardziej spójnym z resztą języka:
Zauważ, że użycie trójskładnikowych operatorów do rozgałęzienia między wyrażeniami jest powszechne w programowaniu funkcjonalnym .
źródło
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.)
. W rzeczywistości używasz dokładnej linii kodu, o której wspomniałem. Ponadto, chociaż ta część pytania jest nie na temat, powiedziałbym, że posuwasz się za daleko w swoich wnioskach. Musisz radykalnie zmienił co kod jest.return false;
vselse { return false; }
, nie dbając o polaryzację gałęzi, dynamiczne wysyłanie, trójskładniki itp. Jeśli piszesz kod proceduralny w języku OO, konieczna jest radykalna zmiana;)if/else
instrukcją i jaka jest biegunowość gałęzi.