Mój kolega twierdzi, że wartości logiczne jako argumenty metod są niedopuszczalne . Zastąpią je wyliczenia. Na początku nie widziałem żadnej korzyści, ale podał mi przykład.
Co jest łatwiejsze do zrozumienia?
file.writeData( data, true );
Lub
enum WriteMode {
Append,
Overwrite
};
file.writeData( data, Append );
Teraz rozumiem! ;-)
Jest to zdecydowanie przykład, w którym wyliczenie jako drugi parametr sprawia, że kod jest znacznie bardziej czytelny.
Jaka jest więc Twoja opinia na ten temat?
coding-style
boolean
enumeration
Thomas Koschel
źródło
źródło
Odpowiedzi:
Wartości logiczne reprezentują opcje „tak / nie”. Jeśli chcesz przedstawić „tak / nie”, użyj wartości logicznej, powinno to być oczywiste.
Ale jeśli jest to wybór między dwiema opcjami, z których żadna nie jest wyraźnie tak lub nie, wówczas wyliczenie może być czasami bardziej czytelne.
źródło
setLightOn(bool)
.Wyliczenia pozwalają również na przyszłe modyfikacje, w których chcesz teraz wybrać trzeci wybór (lub więcej).
źródło
Użyj tego, który najlepiej modeluje Twój problem. W podanym przykładzie wyliczenie jest lepszym wyborem. Jednak byłyby inne sytuacje, w których wartość logiczna jest lepsza. Co ma dla ciebie większy sens:
lub
W tym przypadku mógłbym wybrać opcję logiczną, ponieważ uważam, że jest ona dość jasna i jednoznaczna, i jestem prawie pewien, że mój zamek nie będzie miał więcej niż dwóch stanów. Mimo to drugi wybór jest ważny, ale niepotrzebnie skomplikowany, IMHO.
źródło
Dla mnie ani użycie wartości logicznych, ani wyliczenia nie jest dobrym podejściem. Robert C. Martin bardzo wyraźnie ujmuje to w swojej wskazówce nr 12 z czystego kodu: Eliminacja argumentów boolowskich :
Jeśli metoda robi więcej niż jedną rzecz, powinieneś raczej napisać dwie różne metody, na przykład w twoim przypadku:
file.append(data)
ifile.overwrite(data)
.Użycie wyliczenia nie wyjaśnia sprawy. To niczego nie zmienia, nadal jest argumentem flagowym.
źródło
setVisible(boolean visible) { mVisible = visible; }
. Jaka byłaby alternatywa, którą byś zaproponował?Myślę, że prawie sam na to odpowiedziałeś, myślę, że celem końcowym jest uczynienie kodu bardziej czytelnym, aw tym przypadku wyliczenie to zrobiło, IMO zawsze najlepiej jest spojrzeć na ostateczny cel, a nie ogólne zasady, może pomyśl o tym więcej jako wskazówka, tj. wyliczenia są często bardziej czytelne w kodzie niż ogólne boole, ints itp., ale zawsze będą wyjątki od reguły.
źródło
Pamiętacie pytanie, które Adlai Stevenson zadał ambasadorowi Zorinowi w ONZ podczas kryzysu kubańskiego ?
Jeśli flaga, którą masz w swojej metodzie, ma taki charakter, że możesz ją przypisać do decyzji binarnej , a ta decyzja nigdy nie zmieni się w decyzję trójstronną lub n-kierunkową, wybierz wartość logiczną. Wskazania: Twoja flaga nazywa się isXXX .
Nie rób tego jako logiczne w przypadku czegoś, co jest przełącznikiem trybu . Zawsze jest jeden tryb więcej niż myślałeś, pisząc metodę.
Dylemat jeden więcej trybów dotyczy np. Nawiedzonego systemu Unix, gdzie możliwe tryby uprawnień, które plik lub katalog może mieć obecnie, powodują dziwne podwójne znaczenie trybów w zależności od typu pliku, własności itp.
źródło
Są dwa powody, dla których uważam, że jest to coś złego:
Ponieważ niektórzy ludzie będą pisać metody takie jak:
Jest to oczywiście złe, ponieważ zbyt łatwo jest pomieszać parametry, a nie masz pojęcia, patrząc na to, co określasz. Jednak tylko jeden bool nie jest taki zły.
Ponieważ sterowanie przepływem programu za pomocą prostej gałęzi tak / nie może oznaczać, że masz dwie zupełnie różne funkcje, które są opakowane w jedną w niezręczny sposób. Na przykład:
Naprawdę powinny to być dwie metody
ponieważ kod w nich może być zupełnie inny; być może będą musieli wykonywać różnego rodzaju obsługę błędów i sprawdzanie poprawności, a może nawet będą musieli inaczej formatować dane wychodzące. Nie możesz tego stwierdzić po prostu używając
Write()
lub nawetWrite(Enum.Optical)
(chociaż oczywiście możesz mieć jedną z tych metod, po prostu wywołaj metody wewnętrzne WriteOptical / Mag, jeśli chcesz).Myślę, że to zależy. Nie robiłbym z tego zbyt wielkiego interesu poza numerem 1.
źródło
Wyliczenia są lepsze, ale nie nazwałbym parametrów logicznych jako „niedopuszczalne”. Czasami po prostu łatwiej jest dorzucić jedną małą wartość logiczną i przejść dalej (pomyśl o metodach prywatnych itp.)
źródło
Booleans może być OK w językach, które mają nazwane parametry, takich jak Python i Objective-C, ponieważ nazwa może wyjaśniać, co robi parametr:
lub:
źródło
Nie zgodziłbym się, że to dobra zasada . Oczywiście Enum tworzy w niektórych przypadkach lepszy jawny lub rozwlekły kod, ale z reguły wydaje się, że jest to zbyt daleko idące.
Najpierw weźmy twój przykład: odpowiedzialność (i zdolność) programistów do pisania dobrego kodu nie jest tak naprawdę zagrożona przez parametr boolowski. W twoim przykładzie programista mógłby napisać równie rozwlekły kod, pisząc:
lub wolę bardziej ogólne
Po drugie: przykład Enum, który podałeś, jest tylko „lepszy”, ponieważ przechodzisz przez CONST. Najprawdopodobniej w większości aplikacji przynajmniej niektóre, jeśli nie większość parametrów czasu, które są przekazywane do funkcji, to ZMIENNE. w takim przypadku mój drugi przykład (podawanie zmiennych o dobrych nazwach) jest znacznie lepszy, a Enum dałby ci niewielkie korzyści.
źródło
Wyliczenia mają określoną korzyść, ale nie należy zastępować wszystkich wartości logicznych wyliczeniami. Jest wiele miejsc, w których prawda / fałsz jest właściwie najlepszym sposobem przedstawienia tego, co się dzieje.
Jednak używanie ich jako argumentów metod jest nieco podejrzane, po prostu dlatego, że nie możesz zobaczyć bez wnikania w to, co mają zrobić, ponieważ pozwalają ci zobaczyć, co tak naprawdę oznacza prawda / fałsz
Właściwości (szczególnie w przypadku inicjatorów obiektów C # 3) lub argumenty słów kluczowych (a la ruby lub python) są znacznie lepszym sposobem, aby przejść tam, gdzie w przeciwnym razie użyłbyś argumentu boolowskiego.
Przykład w C #:
Przykład Rubiego
Przykład Pythona
Jedyne, co przychodzi mi do głowy, gdzie argument metody boolowskiej jest właściwy, to java, gdzie nie ma ani właściwości, ani argumentów słów kluczowych. To jeden z powodów, dla których nienawidzę javy :-(
źródło
Chociaż prawdą jest, że w wielu przypadkach wyliczenia są bardziej czytelne i rozszerzalne niż wartości logiczne, absolutna reguła, że „wartości logiczne są niedopuszczalne” jest głupia. Jest nieelastyczny i nieproduktywny - nie pozostawia miejsca na ludzką ocenę. Są podstawowym typem wbudowanym w większości języków, ponieważ są przydatne - rozważ zastosowanie go do innych typów wbudowanych: powiedzenie na przykład „nigdy nie używaj int jako parametru” byłoby po prostu szalone.
Ta zasada jest tylko kwestią stylu, a nie potencjalnych błędów lub wydajności w czasie wykonywania. Lepszą regułą byłoby „preferuj wyliczenia od wartości logicznych ze względu na czytelność”.
Spójrz na framework .Net. Wartości logiczne są używane jako parametry w wielu metodach. Interfejs .Net API nie jest doskonały, ale nie sądzę, aby użycie wartości logicznych jako parametrów było dużym problemem. Etykietka zawsze zawiera nazwę parametru i możesz również utworzyć tego rodzaju wskazówki - wypełnij swoje komentarze XML dotyczące parametrów metody, pojawią się one w etykiecie narzędzia.
Powinienem również dodać, że jest przypadek, kiedy powinieneś wyraźnie refaktoryzować wartości logiczne do wyliczenia - gdy masz dwie lub więcej wartości logicznych w swojej klasie lub w parametrach metody, a nie wszystkie stany są prawidłowe (np. Nie można ich mieć oba mają wartość true).
Na przykład, jeśli twoja klasa ma właściwości takie jak
I błędem jest mieć oba z nich prawdziwe w tym samym czasie, tak naprawdę masz trzy ważne stany, lepiej wyrażone jako coś takiego:
źródło
Niektóre zasady, których Twój kolega mógłby lepiej przestrzegać, to:
źródło
Wartość logiczna byłaby akceptowalna tylko wtedy, gdy nie zamierzasz rozszerzać funkcjonalności frameworka. Enum jest preferowane, ponieważ można rozszerzyć wyliczenie i nie przerywać poprzednich implementacji wywołania funkcji.
Inną zaletą Enum jest to, że jest łatwiejszy do odczytania.
źródło
Jeśli metoda zadaje pytanie takie jak:
gdzie
Wydaje się, że argumenty metod logicznych mają absolutnie doskonały sens.
źródło
To zależy od metody. Jeśli metoda robi coś, co jest w oczywisty sposób prawdą / fałszem, to jest w porządku, np. Poniżej [choć nie mówię, że to najlepszy projekt dla tej metody, to tylko przykład, gdzie użycie jest oczywiste].
Jednak w większości przypadków, takich jak przykład, o którym wspominasz, lepiej jest użyć wyliczenia. W samym .NET Framework istnieje wiele przykładów, w których ta konwencja nie jest przestrzegana, ale dzieje się tak dlatego, że wytyczne projektowe zostały wprowadzone dość późno w cyklu.
źródło
Sprawia to, że rzeczy są nieco bardziej wyraźne, ale zaczyna masowo rozszerzać złożoność twoich interfejsów - w czystym boolowskim wyborze, takim jak dołączanie / nadpisywanie, wydaje się to przesadą. Jeśli potrzebujesz dodać kolejną opcję (o której nie przychodzi mi do głowy w tym przypadku), zawsze możesz wykonać refaktor (w zależności od języka)
źródło
Wyliczenia z pewnością mogą uczynić kod bardziej czytelnym. Jest jeszcze kilka rzeczy, na które należy uważać (przynajmniej w .net)
Ponieważ podstawową pamięcią wyliczenia jest int, domyślną wartością będzie zero, więc należy upewnić się, że 0 jest rozsądną wartością domyślną. (Na przykład struktury mają wszystkie pola ustawione na zero podczas tworzenia, więc nie ma możliwości określenia wartości domyślnej innej niż 0. Jeśli nie masz wartości 0, nie możesz nawet przetestować wyliczenia bez rzutowania na int, co byłoby zły styl.)
Jeśli twoje wyliczenia są prywatne dla twojego kodu (nigdy nie są ujawniane publicznie), możesz przestać czytać tutaj.
Jeśli wyliczenia są publikowane w jakikolwiek sposób w kodzie zewnętrznym i / lub są zapisywane poza programem, rozważ ich jawne numerowanie. Kompilator automatycznie numeruje je od 0, ale jeśli zmienisz układ wyliczeń bez podawania im wartości, możesz skończyć z defektami.
Potrafię legalnie pisać
Aby temu zaradzić, każdy kod, który używa wyliczenia, którego nie można być pewnym (np. Publiczne API), musi sprawdzić, czy wyliczenie jest prawidłowe. Robisz to przez
Jedynym zastrzeżeniem
Enum.IsDefined
jest to, że wykorzystuje odbicie i jest wolniejsze. Występuje również problem z wersjonowaniem. Jeśli chcesz często sprawdzać wartość wyliczenia, lepiej będzie wykonać następujące czynności:Problem z wersjonowaniem polega na tym, że stary kod może wiedzieć, jak obsłużyć tylko 2 wyliczenia, które masz. Jeśli dodasz trzecią wartość, Enum.IsDefined będzie prawdziwe, ale stary kod niekoniecznie może to obsłużyć. Ups.
Jeszcze więcej zabawy można zrobić z
[Flags]
wyliczeniami, a kod weryfikacyjny do tego jest nieco inny.Ja też pamiętać, że do przenoszenia, należy użyć połączenia
ToString()
na wyliczenia i korzystanieEnum.Parse()
podczas czytania ich z powrotem w obu.ToString()
IEnum.Parse()
może obsługiwać[Flags]
enum jest tak dobrze, więc nie ma powodu, aby nie z nich korzystać. Pamiętaj, że to kolejna pułapka, ponieważ teraz nie możesz nawet zmienić nazwy wyliczenia bez możliwego złamania kodu.Więc czasami musisz zważyć wszystkie powyższe, kiedy zadajesz sobie pytanie Czy mogę uciec tylko z boolem?
źródło
IMHO wygląda na to, że wyliczenie byłoby oczywistym wyborem w każdej sytuacji, w której możliwe są więcej niż dwie opcje. Ale zdecydowanie SĄ sytuacje, w których potrzebujesz tylko wartości logicznej. W takim przypadku powiedziałbym, że użycie wyliczenia, w którym działa bool, byłoby przykładem użycia 7 słów, gdy wystarczy 4.
źródło
Wartości logiczne mają sens, gdy masz oczywisty przełącznik, który może być tylko jedną z dwóch rzeczy (tj. Stan żarówki, włączony lub wyłączony). Poza tym dobrze jest napisać to w taki sposób, aby było oczywiste, co przekazujesz - np. Zapisy na dysku - niebuforowane, buforowane wierszowo lub synchroniczne - powinno być przekazane jako takie. Nawet jeśli nie chcesz teraz zezwalać na zapisy synchroniczne (a więc jesteś ograniczony do dwóch opcji), warto rozważyć uczynienie ich bardziej szczegółowymi, aby wiedzieć, co robią na pierwszy rzut oka.
To powiedziawszy, możesz również użyć False i True (boolean 0 i 1), a następnie, jeśli potrzebujesz więcej wartości później, rozszerz funkcję, aby obsługiwać wartości zdefiniowane przez użytkownika (powiedzmy 2 i 3) oraz stare wartości 0/1 będzie ładnie się przenosić, więc Twój kod nie powinien się zepsuć.
źródło
Czasami po prostu łatwiej jest modelować inne zachowanie z przeciążeniami. Kontynuując z twojego przykładu, wyglądałoby tak:
To podejście pogarsza się, jeśli masz wiele parametrów, z których każdy pozwala na ustalony zestaw opcji. Na przykład metoda, która otwiera plik może mieć kilka permutacji trybu pliku (otwórz / utwórz), dostępu do pliku (odczyt / zapis), tryb współdzielenia (brak / odczyt / zapis). Całkowita liczba konfiguracji jest równa iloczynom kartezjańskim poszczególnych opcji. Oczywiście w takich przypadkach wielokrotne przeciążenia nie są właściwe.
Wyliczenia mogą, w niektórych przypadkach, uczynić kod bardziej czytelnym, chociaż sprawdzanie poprawności dokładnej wartości wyliczenia w niektórych językach (na przykład C #) może być trudne.
Często parametr boolowski jest dołączany do listy parametrów jako nowe przeciążenie. Jednym z przykładów w .NET jest:
To drugie przeciążenie stało się dostępne w nowszej wersji platformy .NET niż pierwsza.
Jeśli wiesz, że będą tylko dwie możliwości, wartość logiczna może być w porządku. Wyliczenia są rozszerzalne w sposób, który nie zepsuje starego kodu, chociaż stare biblioteki mogą nie obsługiwać nowych wartości wyliczeniowych, więc nie można całkowicie zignorować wersji.
EDYTOWAĆ
W nowszych wersjach języka C # można używać nazwanych argumentów, które IMO mogą sprawić, że wywołanie kodu będzie wyraźniejsze w taki sam sposób, jak wyliczenia. Korzystając z tego samego przykładu co powyżej:
źródło
Tam, gdzie zgadzam się, że wyliczenia są dobrym rozwiązaniem, w metodach, w których masz 2 opcje (i tylko dwie opcje możesz mieć czytelność bez wyliczenia).
na przykład
Uwielbiam Enum, ale wartości logiczne też są przydatne.
źródło
To jest późny wpis w starym poście i jest tak daleko w dół strony, że nikt go nigdy nie przeczyta, ale ponieważ nikt już tego nie powiedział ....
Komentarz w tekście znacznie ułatwia rozwiązanie nieoczekiwanego
bool
problemu. Oryginalny przykład jest szczególnie ohydny: wyobraź sobie, że próbujesz nazwać zmienną w deklaracji funkcji! To byłoby coś takiegoAle dla przykładu, powiedzmy, że to jest deklaracja. Następnie, dla niewyjaśnionego inaczej argumentu logicznego, umieściłem nazwę zmiennej w komentarzu w wierszu. Porównać
z
źródło
To naprawdę zależy od dokładnej natury argumentu. Jeśli nie jest to tak / nie lub prawda / fałsz, to wyliczenie czyni go bardziej czytelnym. Ale w przypadku wyliczenia musisz sprawdzić argument lub mieć akceptowalne domyślne zachowanie, ponieważ można przekazać niezdefiniowane wartości typu podstawowego.
źródło
Użycie w przykładzie wyliczeń zamiast wartości logicznych pomaga uczynić wywołanie metody bardziej czytelną. Jednak jest to substytut mojego ulubionego elementu życzeń w C #, nazwanych argumentów w wywołaniach metod. Ta składnia:
byłby doskonale czytelny i można by wtedy zrobić to, co powinien zrobić programista, czyli wybrać najbardziej odpowiedni typ dla każdego parametru w metodzie, bez względu na to, jak wygląda w IDE.
C # 3.0 zezwala na nazwane argumenty w konstruktorach. Nie wiem, dlaczego nie mogą tego zrobić również metodami.
źródło
Tylko wartości logiczne
true
/false
. Nie jest więc jasne, co to oznacza.Enum
może mieć znaczącą nazwę, na przykładOVERWRITE
,APPEND
itd enums tak są lepsze.źródło