Przypisanie wartości logicznej najlepszych praktyk [zamknięte]

10

W programie przejętym od innego programisty natknąłem się na następujące warunki:

if (obj.Performance <= LOW_PERFORMANCE)
{
    obj.NeedsChange = true;
}
else
{
    obj.NeedsChange = false;
}

Uważam, że ten kod jest zbędny i brzydki, więc zmieniłem go na coś, co moim zdaniem było prostym zadaniem logicznym opartym na porównaniu:

obj.NeedsChange = obj.Performance <= LOW_PERFORMANCE;

Widząc to, ktoś sprawdzający mój kod skomentował, że chociaż moja zmiana jest funkcjonalnie poprawna, może to mylić kogoś innego, kto na nią patrzy. Uważa, że ​​użycie operatora trójskładnikowego czyni to zadanie bardziej zrozumiałym, podczas gdy nie podoba mi się dodanie bardziej redundantnego kodu:

obj.NeedsChange = (obj.Performance <= LOW_PERFORMANCE) ? true : false;

Jego rozumowanie jest takie, że robienie czegoś w najbardziej zwięzły sposób nie jest tego warte, jeśli powoduje to, że inny programista musi się zatrzymać i zastanowić się, co dokładnie zrobiłeś.

Prawdziwe pytanie brzmi: która z tych trzech metod przypisywania wartości do wartości logicznej obj.NeedsChangejest najbardziej przejrzysta i najłatwiejsza do utrzymania?

Zach Posten
źródło
25
Trzecia forma jest absurdalna; po prostu stwierdza, co powinno już być rażąco oczywiste w drugiej formie.
Robert Harvey,
6
To zależy wyłącznie od osobistych preferencji. Możemy udawać, że jest inaczej, ale ponieważ są one funkcjonalnie równoważne, sprowadza się to do stylu . Jasne, jest różnica w czytelności, ale moją „czytelną i przejrzystą” może być twoja „
rozwarta
3
@scriptin 5-8 linii v 1 linia to więcej niż preferencja, linijka 5-8 jest zwykle wyraźniejsza i lepsza. W tym prostym przykładzie wolę linię 1, ale ogólnie widziałem zbyt wiele 10 wkładek, które zostały zaciemnione w 1-liniowym dla wygody. Biorąc to pod uwagę, nigdy nie narzekałbym na wariant 1, może nie jest ładny, ale spełnia swoje zadanie równie wyraźnie i oczywiście.
gbjbaanb
4
Opcje 1 i 3 mówią do mnie „Autor tak naprawdę nie rozumie logiki logicznej”.
17 z 26
2
Wariant 1 może być przydatny, jeśli często trzeba ustawić punkt przerwania, który zależy od wartości.
Ian

Odpowiedzi:

39

Wolę 2, ale może pójdę na drobną korektę:

obj.NeedsChange = ( obj.Performance <= LOW_PERFORMANCE );

Dla mnie nawiasy ułatwiają parsowanie linii i na pierwszy rzut oka widać, że przypisujesz wynik porównania, a nie podwójnego przypisania. Nie jestem pewien, dlaczego tak jest (ponieważ nie mogę wymyślić języka, w którym nawiasy faktycznie zapobiegałyby podwójnemu przypisaniu), ale jeśli musisz zadowolić swojego recenzenta, być może będzie to akceptowalny kompromis.

Jacob Raihle
źródło
4
to poprawna odpowiedź - chociaż kod w pytaniu jest poprawny, dodanie nawiasów powie czytelnikowi, że nie jest to zadanie. Jeśli szybko przeglądałeś kod, nawiasy kwadratowe dają ci natychmiastowe dodatkowe informacje, które powstrzymują cię od bliższego sprawdzenia, czy kod ma być taki, a nie był przypadkowym błędem. Na przykład, wyobraź sobie, że linia brzmiała a = b == c: czy miałeś na myśli przypisanie bool, czy miałeś na myśli przypisanie c do b i a.
gbjbaanb
Nawiasy zapobiegałyby podwójnemu przypisaniu w Pythonie. Nawet w językach, w których nie zapobiegają podwójnemu przypisaniu, nawiasy zdecydowanie wskazują, że masz do czynienia z dwoma rodzajami operacji.
user2357112 obsługuje Monikę
23

Wariant 1 jest łatwy do zrozumienia, ale to jego jedyna zaleta. Automatycznie zakładam, że każdy, kto pisze w ten sposób, tak naprawdę nie rozumie, o co chodzi w booleanach i pod wieloma innymi względami będzie pisać podobnie infantylny kod.

Wariant 2 jest tym, co zawsze bym pisał i spodziewałem się przeczytać. Myślę, że każdy, kto jest zdezorientowany tym idiomem, nie powinien być profesjonalnym pisarzem oprogramowania.

Wariant 3 łączy wady zarówno 1, jak i 2.

Kilian Foth
źródło
Cóż, wariant 1 ma tę zaletę, co wariant 2 ...
Deduplicator
1
+1 za kod infantylny. Patrzyłem na taki kod od lat, po prostu brakowało mi odpowiedniego słowa, aby go zidentyfikować.
Lilienthal
1
Moje pierwsze założenie z kodem takim jak Wariant 1 jest takie, że dwie gałęzie w jednym punkcie w przeszłości były bardziej skomplikowane i ktoś nie zwracał uwagi podczas refaktoryzacji. Jeśli jednak tak było, kiedy napisano po raz pierwszy, to zgadzam się z „niezrozumieniem
booleanów
13

Kod w dowolnym momencie jest bardziej skomplikowany niż wymaga uruchomienia „co to ma robić?” zapach w czytniku.

Na przykład pierwszy przykład sprawia, że ​​zastanawiam się: „czy w instrukcji if / else była już inna funkcjonalność w pewnym momencie, który został usunięty?”

Przykład (2) jest prosty, jasny i robi dokładnie to, co jest potrzebne. Przeczytałem go i od razu zrozumiałem, co robi kod.

Dodatkowy puch w (3) spowodowałby, że zastanawiałem się, dlaczego autor napisał to w ten sposób zamiast (2). Nie powinno być powodem, ale w tym przypadku nie wydaje się być, więc to nie jest w ogóle pomocny i trudniejsze do odczytania, ponieważ składnia sugeruje coś prezent, który nie istnieje. Próba nauczenia się, co jest obecne (kiedy nic nie jest), utrudnia czytanie kodu.

kraina krańca
źródło
2

Łatwo zauważyć, że Wariant 2 i Wariant 1 są powiązane poprzez szereg oczywistych i prostych refaktoryzacji:

if (obj.Performance <= LOW_PERFORMANCE)
{
    obj.NeedsChange = true;
}
else
{
    obj.NeedsChange = false;
}

Tutaj mamy niepotrzebne powielanie kodu, możemy wyróżnić przypisanie:

obj.NeedsChange = if (obj.Performance <= LOW_PERFORMANCE)
{
    true
}
else
{
    false
}

lub napisane bardziej zwięźle:

obj.NeedsChange = if (obj.Performance <= LOW_PERFORMANCE) true else false

Teraz powinno być od razu oczywiste, że to przypisze true, jeśli warunek jest prawdziwy, i false, jeśli warunek jest false, IOW po prostu przypisze wartość warunku, tzn. Jest równoważny

obj.NeedsChange = obj.Performance <= LOW_PERFORMANCE

Warianty 1 i 3 są typowym kodem nowicjusza napisanym przez kogoś, kto nie rozumie, jaka jest wartość zwracana porównania.

Jörg W Mittag
źródło
Dodałbym twoją (...) ... fałszywą część jako komentarz tuż przed ładnym, wtedy uzyskasz prosty skan poprzez przejrzystość kodu i lepszy kod.
DaveM,
2

Chociaż programowanie powinno zmierzać w kierunku wyraźnego nad niejawna „jakby facet, który kończy się utrzymując swój kod będzie brutalny psychopata, który wie, gdzie mieszkasz”, ty może przyjąć kilka podstawowych rzeczy , że psycho następca będzie właściwy w.

Jednym z nich jest składnia języka, którego używa.

obj.NeedsChange = obj.Performance <= LOW_PERFORMANCE;

jest bardzo jasne dla każdego, kto zna składnię C / C ++ / C # / Java / JavaScript.

Jest także znacznie bardziej czytelny niż 8 linii.

if (obj.Performance <= LOW_PERFORMANCE)
{
    obj.NeedsChange = true;
}
else
{
    obj.NeedsChange = false;
}

i mniej podatne na błędy

if (obj.Performance <= LOW_PERFORMANCE)
{
    obj.NeedsChange = true;
}
else
{
    obj.Needschange = false;
}

Jest to lepsze niż dodawanie niepotrzebnych znaków, jakbyś na wpół zapomniał składni języka:

obj.NeedsChange = obj.Performance <= LOW_PERFORMANCE ? true : false;

obj.NeedsChange = (obj.Performance <= LOW_PERFORMANCE);

Myślę, że jest wiele błędów w składni podobnej do C w wielu językach: niespójna kolejność operacji, niespójna asocjacja lewo / prawo, przeciążone użycie symboli, nawiasy klamrowe / wcięcia, operatory trójskładnikowe, notacja infiksowa itp.

Ale rozwiązaniem nie jest wymyślenie własnej, zastrzeżonej wersji. W ten sposób leży szaleństwo, ponieważ każdy tworzy swój własny.


Zasadniczo najważniejszą rzeczą, która sprawia, że kod Real World TM jest nieczytelny, jest jego ilość.

Między niepatologicznym programem z 200 liniami a trywialnie identycznym programem z 1600 liniami, ten krótszy prawie zawsze łatwiej będzie przeanalizować i zrozumieć. Z radością powitałbym Twoją zmianę każdego dnia.

Paul Draper
źródło
1

Większość programistów będzie w stanie zrozumieć drugą formę na pierwszy rzut oka. Moim zdaniem uproszczenie w pierwszej formie jest po prostu niepotrzebne.

Możesz poprawić czytelność, dodając spacje i nawiasy klamrowe, takie jak:

obj.NeedsChange =    obj.Performance <= LOW_PERFORMANCE;

lub

obj.NeedsChange = ( obj.Performance <= LOW_PERFORMANCE );

jak wspomniał Jacob Raihle.

Uday Shankar
źródło