Załóżmy na przykład, że mam klasę Member
, która ma lastChangePasswordTime:
class Member{
.
.
.
constructor(){
this.lastChangePasswordTime=null,
}
}
którego lastChangePasswordTime może być nieobecny, ponieważ niektórzy członkowie mogą nigdy nie zmieniać swoich haseł.
Ale zgodnie z Jeśli wartości zerowe są złe, co należy zastosować, gdy wartość może być znacząco nieobecna? i https://softwareengineering.stackexchange.com/a/12836/248528 , nie powinienem używać wartości null do reprezentowania istotnie nieobecnej wartości. Próbuję więc dodać flagę logiczną:
class Member{
.
.
.
constructor(){
this.isPasswordChanged=false,
this.lastChangePasswordTime=null,
}
}
Ale myślę, że jest dość przestarzały, ponieważ:
Gdy isPasswordChanged ma wartość false, lastChangePasswordTime musi mieć wartość null, a sprawdzenie lastChangePasswordTime == null jest prawie identyczne jak sprawdzenie isPasswordChanged ma wartość false, więc wolę sprawdzić lastChangePasswordTime == null bezpośrednio
Zmieniając tutaj logikę, mogę zapomnieć o aktualizacji obu pól.
Uwaga: gdy użytkownik zmienia hasło, zapisuje czas w następujący sposób:
this.lastChangePasswordTime=Date.now();
Czy dodatkowe pole boolowskie jest lepsze niż odwołanie zerowe?
std::optional
lubOption
. W innych językach może być konieczne zbudowanie odpowiedniego mechanizmu lub skorzystanie znull
czegoś podobnego, ponieważ jest to bardziej idiomatyczne.lastChangePasswordTime
może być tam niezainicjowanym wskaźnikiem, a porównywanie go do czegokolwiek byłoby zachowaniem niezdefiniowanym. Nie jest to naprawdę ważny powód, aby nie inicjować wskaźnika doNULL
/nullptr
zamiast, szczególnie nie we współczesnym C ++ (gdzie w ogóle nie używałbyś wskaźnika), ale kto wie? Innym przykładem mogą być języki bez wskaźników lub złą obsługą wskaźników. (Przychodzi na myśl FORTRAN 77 ...)Odpowiedzi:
Nie rozumiem, dlaczego, jeśli masz wyraźnie nieobecną wartość,
null
nie należy jej używać, jeśli jesteś rozmyślny i ostrożny.Jeśli Twoim celem jest otoczenie wartości zerowalnej, aby zapobiec jej przypadkowemu odwołaniu, sugerowałbym utworzenie
isPasswordChanged
wartości jako funkcji lub właściwości, która zwraca wynik kontroli zerowej, na przykład:Moim zdaniem robienie tego w ten sposób:
isPasswordChanged
wartości.Sposób przechowywania danych (przypuszczalnie w bazie danych) byłby odpowiedzialny za zapewnienie zachowania wartości zerowych.
źródło
isPasswordChanged
tak jak możesz zapomnieć, aby sprawdzić, czy jest zerowy. Nic tu nie zyskałem.nulls
nie są złe. Używanie ich bez myślenia jest. Jest to przypadek, w którymnull
jest poprawna odpowiedź - nie ma daty.Pamiętaj, że twoje rozwiązanie stwarza więcej problemów. Jakie jest znaczenie ustawienia daty na coś, ale
isPasswordChanged
jest to fałsz? Właśnie stworzyłeś przypadek sprzecznych informacji, które musisz specjalnie przechwycić i potraktować, podczas gdynull
wartość ma jasno określone, jednoznaczne znaczenie i nie może być sprzeczna z innymi informacjami.Więc nie, twoje rozwiązanie nie jest lepsze. Przyjęcie
null
wartości jest tutaj właściwym podejściem. Ludzie, którzy twierdzą, żenull
zawsze jest zły, bez względu na kontekst, nie rozumieją, dlaczegonull
istnieje.źródło
null
istnieje, ponieważ Tony Hoare popełnił błąd o wartości miliarda dolarów w 1965 roku. Ludzie, którzy twierdzą, żenull
to „zło”, nadmiernie upraszczają ten temat, ale dobrze, że współczesne języki lub style programowania odchodząnull
, zastępując z dedykowanymi typami opcji.DateTime
pole jest wskaźnikiem. Cała koncepcjanull
ma sens tylko dla wskaźników!W zależności od języka programowania mogą istnieć dobre alternatywy, takie jak
optional
typ danych. W C ++ (17) byłoby to std :: opcjonalne . Może być nieobecny lub mieć dowolną wartość bazowego typu danych.źródło
Optional
w java; znaczenie zerowejOptional
zależy od ciebie ...Optional<Date> lastPasswordChangeTime = null;
, ale nie poddałbym się tylko z tego powodu. Zamiast tego zaszczepiłbym bardzo mocno trzymaną zasadę zespołu, że nigdy nie można przypisywać żadnych wartości opcjonalnychnull
. Opcjonalnie kupuje zbyt wiele fajnych funkcji, aby łatwo z nich zrezygnować. W prosty sposób można przypisać wartości domyślne (orElse
lub leniwe oceny:orElseGet
), błędy throw (orElseThrow
) przekształca wartości w sposób bezpieczny zerowej (map
,flatmap
), itd.isPresent
jest, moim zdaniem, prawie zawsze zapachem kodu i dość wiarygodnym znakiem, że twórcy używający tych opcji nie mają racji . Rzeczywiście, w zasadzie nie daje to żadnych korzyściref == null
, ale nie jest to również coś, czego powinieneś często używać. Nawet jeśli zespół jest niestabilny, narzędzie do analizy statycznej może ustanowić prawo, takie jak linijka lub FindBugs , które mogą wychwycić większość przypadków.null
, ponieważ język jest w 100% kompatybilny z Java, ale nikt nie używa go, iOption[T]
to wszędzie, z doskonałą obsługą funkcjonalnego programowania jakmap
,flatMap
, dopasowywania wzorców i tak dalej, który jedzie daleko, daleko poza== null
kontrolami. Masz rację, że teoretycznie typ opcji brzmi jak chwalebny wskaźnik, ale rzeczywistość dowodzi, że ten argument jest błędny.Poprawnie używając null
Istnieją różne sposoby korzystania
null
. Najczęstszym i semantycznie poprawnym sposobem jest użycie go, gdy możesz mieć lub nie mieć jednej wartości. W tym przypadku wartość jest równanull
lub jest czymś znaczącym, jak zapis z bazy danych lub coś takiego.W takich sytuacjach najczęściej używasz go w następujący sposób (w pseudokodzie):
Problem
I ma bardzo duży problem. Problem polega na tym, że do czasu wywołania
doSomethingUseful
wartość mogła nie zostać sprawdzonanull
! Jeśli tak nie było, program najprawdopodobniej ulegnie awarii. A użytkownik może nawet nie zobaczyć żadnych dobrych komunikatów o błędach, pozostawiając coś w rodzaju „strasznego błędu: pożądana wartość, ale dostała zero!” (po aktualizacji: chociaż może występować jeszcze mniej informacji, takich jakSegmentation fault. Core dumped.
lub, co gorsza, brak błędów i nieprawidłowe manipulowanie wartością NULL w niektórych przypadkach)Niezwykle częstym błędem jest zapominanie o pisaniu czeków
null
i radzeniu sobie wnull
sytuacjach . Właśnie dlatego Tony Hoare, który wynalazł, powiedział podczas konferencji programowej QCon London w 2009 roku, że popełnił błąd miliarda dolarów w 1965 roku: https://www.infoq.com/presentations/Null-References-The-Billion-Dollar- Błąd-Tony-Hoarenull
Unikanie problemu
Niektóre technologie i języki sprawiają, że sprawdzanie pod kątem
null
niemożności zapomnienia na różne sposoby, zmniejsza liczbę błędów.Na przykład Haskell ma
Maybe
monadę zamiast zer. Załóżmy, żeDatabaseRecord
jest to typ zdefiniowany przez użytkownika. W Haskell wartość typuMaybe DatabaseRecord
może być równaJust <somevalue>
lub może być równaNothing
. Możesz następnie używać go na różne sposoby, ale bez względu na to, jak go używasz, nie możesz zastosować niektórych operacjiNothing
bez wiedzy o tym.Na przykład ta funkcja o nazwie
zeroAsDefault
zwracax
dlaJust x
i0
dlaNothing
:Christian Hackl mówi, że C ++ 17 i Scala mają swoje własne sposoby. Możesz więc spróbować dowiedzieć się, czy twój język ma coś takiego i użyć go.
Wartości zerowe są nadal w powszechnym użyciu
Jeśli nie masz nic lepszego, używanie
null
jest w porządku. Tylko uważaj na to. W każdym razie pomocne będą deklaracje typu w funkcjach.Może to również brzmieć niezbyt progresywnie, ale powinieneś sprawdzić, czy twoi koledzy chcą z niego skorzystać,
null
czy czegoś innego. Mogą być konserwatywne i z pewnych powodów mogą nie chcieć korzystać z nowych struktur danych. Na przykład obsługa starszych wersji języka. Takie rzeczy powinny być zadeklarowane w standardach kodowania projektu i odpowiednio omówione z zespołem.Na twoją propozycję
Sugerujesz użycie osobnego pola boolowskiego. Ale i tak musisz to sprawdzić i nadal możesz zapomnieć o sprawdzeniu. Więc nic tu nie wygrało. Jeśli możesz nawet zapomnieć o czymś innym, takim jak aktualizacja obu wartości za każdym razem, jest jeszcze gorzej. Jeśli problem zapomnienia o sprawdzeniu
null
nie zostanie rozwiązany, nie ma sensu. Unikanienull
jest trudne i nie należy robić tego w sposób, który pogorszy sytuację.Jak nie używać null
Wreszcie istnieją powszechne sposoby
null
nieprawidłowego użycia . Jednym z takich sposobów jest użycie go zamiast pustych struktur danych, takich jak tablice i łańcuchy. Pusta tablica jest właściwą tablicą, jak każda inna! Prawie zawsze jest ważne i przydatne dla struktur danych, które mogą pasować do wielu wartości, aby mogły być puste, tzn. Mają zerową długość.Z punktu widzenia algebry pusty ciąg znaków dla ciągów jest podobny do 0 dla liczb, tj. Tożsamość:
Pusty ciąg znaków pozwala ogólnie stać się monoidem: https://en.wikipedia.org/wiki/Monoid Jeśli go nie dostaniesz, nie jest to dla ciebie ważne.
Zobaczmy teraz, dlaczego jest to ważne dla programowania w tym przykładzie:
Jeśli podamy tutaj pustą tablicę, kod będzie działał poprawnie. Po prostu nic nie zrobi. Jeśli jednak przejdziemy
null
tutaj, prawdopodobnie nastąpi awaria z błędem typu „przepraszam, nie można przepuścić przez zero”. Możemy to owinąć,if
ale to jest mniej czyste i znowu, możesz zapomnieć to sprawdzićJak obsłużyć zerowy
To, co
doSomethingAboutIt()
powinno być zrobione, a zwłaszcza czy powinno to spowodować wyjątek, to kolejna skomplikowana kwestia. Krótko mówiąc, zależy to od tego, czynull
była to dopuszczalna wartość wejściowa dla danego zadania i czego oczekuje się w odpowiedzi. Wyjątek stanowią zdarzenia, których nie oczekiwano. Nie zajmę się tym tematem. Ta odpowiedź jest już bardzo długa.źródło
horrible error: wanted value but got null!
jest znacznie lepszy niż bardziej typowySegmentation fault. Core dumped.
...Error: the product you want to buy is out of stock
.null
gdzienull
jest niedozwolone jest błędem programowania, tj. Błędem w kodzie. Produkt niedostępny w magazynie jest częścią zwykłej logiki biznesowej i nie jest błędem. Nie można i nie należy próbować tłumaczyć wykrycia błędu na normalną wiadomość w interfejsie użytkownika. Natychmiastowe zawieszanie się i niewykonywanie większej ilości kodu jest preferowanym sposobem działania, szczególnie jeśli jest to ważna aplikacja (np. Wykonująca prawdziwe transakcje finansowe).null
całkowicie usunąć się nawet z tła.Oprócz wszystkich bardzo dobrych wcześniej udzielonych odpowiedzi dodam, że za każdym razem, gdy kusi cię, aby pole było puste, zastanów się, czy może to być typ listy. Typ zerowalny jest równoważnie listą 0 lub 1 elementów i często można go uogólnić na listę N elementów. Szczególnie w tym przypadku warto rozważyć
lastChangePasswordTime
utworzenie listypasswordChangeTimes
.źródło
Zadaj sobie następujące pytanie: które zachowanie wymaga pola lastChangePasswordTime?
Jeśli potrzebujesz tego pola dla metody IsPasswordExpired (), aby ustalić, czy Członek powinien być monitowany o zmianę hasła co jakiś czas, ustawiłbym w polu czas, w którym Członek był początkowo tworzony. Implementacja IsPasswordExpired () jest taka sama dla nowych i istniejących członków.
Jeśli masz osobny wymóg, że nowo utworzeni członkowie muszą zaktualizować swoje hasło, dodam osobne pole boolowskie o nazwie passwordShouldBeChanged i ustawię wartość true podczas tworzenia. Chciałbym wtedy zmienić funkcjonalność metody IsPasswordExpired (), aby uwzględnić sprawdzanie tego pola (i zmienić nazwę metody na ShouldChangePassword).
Wyraź swoje intencje w kodzie.
źródło
Po pierwsze, zero jako zło jest dogmatem i jak zwykle z dogmatem działa najlepiej jako wytyczna, a nie jako test pozytywny / negatywny.
Po drugie, możesz przedefiniować swoją sytuację w taki sposób, że ma sens, aby wartość nigdy nie była zerowa. InititialPasswordChanged to wartość logiczna początkowo ustawiona na wartość false, PasswordSetTime to data i godzina ustawienia bieżącego hasła.
Należy pamiętać, że chociaż wiąże się to z niewielkim kosztem, możesz ZAWSZE obliczyć, ile czasu minęło od ostatniego ustawienia hasła.
źródło
Oba są „bezpieczne / zdrowe / prawidłowe”, jeśli dzwoniący sprawdzi przed użyciem. Problem polega na tym, że dzwoniący nie sprawdzi. Co jest lepsze, jakiś smak błędu zerowego lub użycie niepoprawnej wartości?
Nie ma jednej poprawnej odpowiedzi. To zależy od tego, czym się martwisz.
Jeśli awarie są naprawdę złe, ale odpowiedź nie jest krytyczna lub ma zaakceptowaną wartość domyślną, być może lepiej jest użyć wartości logicznej jako flagi. Jeśli użycie niewłaściwej odpowiedzi jest gorszym problemem niż awaria, lepsze jest użycie wartości null.
W większości „typowych” przypadków szybka awaria i zmuszanie dzwoniącego do sprawdzenia jest najszybszym sposobem na rozsądny kod, a zatem uważam, że domyślnym wyborem powinna być null. Nie uwierzyłbym jednak zbytnio w ewangelistów „X jest źródłem wszelkiego zła”; zwykle nie przewidzieli wszystkich przypadków użycia.
źródło