Czy lepiej jest zwracać NULL lub puste wartości z funkcji / metod, w których zwracana wartość nie jest obecna?

135

Szukam rekomendacji tutaj. Mam problem z tym, czy lepiej zwrócić NULL, czy pustą wartość z metody, gdy zwracana wartość nie jest obecna lub nie można jej ustalić.

Jako przykład weź następujące dwie metody:

string ReverseString(string stringToReverse) // takes a string and reverses it.
Person FindPerson(int personID)    // finds a Person with a matching personID.

W ReverseString(), powiedziałbym, że return pusty łańcuch, ponieważ typem zwracanym jest string, więc osoba dzwoniąca oczekuje tego. W ten sposób osoba dzwoniąca nie będzie musiała sprawdzać, czy zwrócono NULL.

W przypadku FindPerson()powrotu wartości NULL wydaje się lepsze dopasowanie. Niezależnie od tego, czy new Person()zwracany jest NULL, czy pusty Obiekt Person ( ), osoba dzwoniąca musi sprawdzić, czy Obiekt Person ma wartość NULL lub jest pusta, zanim cokolwiek z tym zrobi (np. Wywołanie UpdateName()). Dlaczego więc nie po prostu zwrócić tutaj NULL, a wtedy dzwoniący musi tylko sprawdzić, czy NULL.

Czy ktoś ma z tym problem? Każda pomoc lub wgląd są mile widziane.

PB
źródło
2
Będzie się różnić. Można również argumentować, że metoda powinna zatrzymać wartości, które są niepoprawne z góry, na przykład parametr stringToReverse jest przekazywany jako pusty lub zerowy. Jeśli wykonałeś tę kontrolę (odrzuciłeś wyjątek), zawsze zwróci ona wartość inną niż null.
Aaron McIver
Fowler POEAA (2) 496 wzorców bazowych - przypadek specjalny (obiekt zerowy) Bloch Effective Java, 2. edycja, pozycja 43: Zwróć puste tablice lub kolekcje, a nie wartości zerowe
Boris Treukhov,
1
@ Boris Nie widziałem twojego komentarza. Właściwie właśnie opublikowałem Special Case jako odpowiedź.
Thomas Owens
@ Thomas Nie widzę w tym żadnego problemu :). Co do pytania, to i tak kwestia zdrowego rozsądku.
Boris Treukhov,
Ciekawym komentarzem jest to, że w bazach danych Oracle wartość NULL i pusty ciąg znaków są takie same
WuHoUnited

Odpowiedzi:

77

StackOverflow ma dobrą dyskusję na ten właśnie temat w tym pytaniu i odpowiedziach . W najwyżej ocenianym pytaniu Kronoz zauważa:

Zwrócenie wartości null jest zwykle najlepszym pomysłem, jeśli zamierzasz wskazać, że żadne dane nie są dostępne.

Pusty obiekt oznacza, że ​​dane zostały zwrócone, a zwrócenie wartości null wyraźnie wskazuje, że nic nie zostało zwrócone.

Ponadto zwrócenie wartości NULL spowoduje wyjątek NULL, jeśli spróbujesz uzyskać dostęp do elementów w obiekcie, co może być przydatne do wyróżnienia błędnego kodu - próba uzyskania dostępu do elementu niczego nie ma sensu. Dostęp do członków pustego obiektu nie zawiedzie, co oznacza, że ​​błędy mogą pozostać nieodkryte.

Osobiście lubię zwracać puste ciągi dla funkcji, które zwracają ciągi, aby zminimalizować ilość błędów, które należy wprowadzić. Musisz jednak upewnić się, że grupa, z którą pracujesz, będzie przestrzegać tej samej konwencji - w przeciwnym razie korzyści wynikające z tej decyzji nie zostaną osiągnięte.

Jednak, jak zaznaczono w plakacie w odpowiedzi SO, wartości zerowe powinny być prawdopodobnie zwrócone, jeśli obiekt jest oczekiwany, aby nie było wątpliwości, czy dane są zwracane.

W końcu nie ma jednego najlepszego sposobu na robienie rzeczy. Budowanie konsensusu zespołu ostatecznie doprowadzi do najlepszych praktyk zespołu.

JW8
źródło
8
Osobiście lubię zwracać puste ciągi dla funkcji, które zwracają ciągi, aby zminimalizować ilość błędów, które należy wprowadzić. Nigdy nie zrozumiałem tego argumentu. Wyzerowanie jest co najwyżej <10 znaków? Dlaczego działamy tak, jakby to było zginanie pleców?
Aaron McIver
19
Nikt nie powiedział, że to zginanie siły roboczej. Ale to jest praca. Dodaje specjalny kod do kodu, co oznacza jeszcze jedną gałąź do przetestowania. Ponadto zakłóca przepływ kodu. for x in list_of_things() {...}jest prawdopodobnie szybszy do zdobycial = list_of_things(); if l != null {...}
Bryan Oakley,
5
@Bryan: istnieje duża różnica między pustą listą wartości a pustym łańcuchem. Ciąg rzadko reprezentuje listę znaków.
kevin cline
3
@kevin cline: oczywiście. Ale w przypadku łańcucha możesz chcieć, aby ten łańcuch pojawił się w logu, niezależnie od tego, czy jest pusty, czy nie. Konieczność sprawdzenia, czy jest ona pusta, aby można było wydrukować pusty ciąg, zaciemnia prawdziwy zamiar. A może to coś w rodzaju pola bazy danych, które zawiera treści dodane przez użytkowników, które chcesz dodać do strony internetowej. Łatwiej to zrobić emit(user.hometown)niżif user.hometown == null { emit("") else {emit(user.hometown)}
Bryan Oakley,
1
Nie tylko jest to dodatkowe pisanie (nic wielkiego), ale wpływa na czytelność. Osobiście uważam, że kod rozpraszający jest przez zestaw zerowych czeków.
ToddR
82

W całym kodzie, który piszę, unikam powrotu nullz funkcji. Przeczytałem to w Clean Code .

Problem z używaniem nullpolega na tym, że osoba korzystająca z interfejsu nie wie, czy nulljest to możliwy wynik i czy musi to sprawdzić, ponieważ nie ma not nulltypu odwołania.

W F # można zwracać optiontyp, który może być some(Person)albo none, więc to oczywiste dla osoby dzwoniącej, że trzeba sprawdzić.

Analogiczny wzorzec C # (anty-) to Try...metoda:

public bool TryFindPerson(int personId, out Person result);

Teraz wiem, że ludzie powiedzieli, że nienawidzą tego Try...wzorca, ponieważ parametr wyjściowy łamie idee czystej funkcji, ale tak naprawdę nie różni się od:

class FindResult<T>
{
   public FindResult(bool found, T result)
   {
       this.Found = found;
       this.Result = result;
   }

   public bool Found { get; private set; }
   // Only valid if Found is true
   public T Result { get; private set;
}

public FindResult<Person> FindPerson(int personId);

... i szczerze mówiąc, możesz założyć, że każdy programista .NET wie o Try...wzorcu, ponieważ jest on używany wewnętrznie przez platformę .NET. Oznacza to, że nie muszą czytać dokumentacji, aby zrozumieć, co ona robi, co jest dla mnie ważniejsze niż trzymanie się purystycznego poglądu na funkcje (rozumienie, że resultjest to outparametr, a nie refparametr).

Więc poszedłbym z tym, TryFindPersonponieważ wydaje ci się, że to zupełnie normalne, że nie można go znaleźć.

Jeśli z drugiej strony nie ma logicznego powodu, że dzwoniący dostarczyłby taki, personIdktóry nie istniał, prawdopodobnie zrobiłbym to:

public Person GetPerson(int personId);

... a potem rzuciłbym wyjątek, jeśli byłby nieważny. Get...Prefiks oznacza, że rozmówca wie, powinien on odnieść sukces.

Scott Whitlock
źródło
28
+1, odpowiedziałbym na to pytanie, odnosząc się do Clean Code, jeśli jeszcze tego nie zrobiłeś. Podoba mi się mantra: „JEŻELI TWOJA FUNKCJA NIE MOŻE ZROBIĆ, CO MÓWI NAZWA, NASTĘPNIE WYRZUCAJ WYJĄTEK”. Muuuch lepiej niż sprawdzanie wartości zerowej co kilkanaście linii ...
Graham,
13
Zrezygnowałem z założenia czegoś o wiedzy ludzi.
CaffGeek,
5
„Problem z używaniem wartości NULL polega na tym, że osoba korzystająca z interfejsu nie wie, czy NULL jest możliwym wynikiem i czy musi to sprawdzić, ponieważ nie ma typu odwołania zerowego.” Ponieważ typy referencyjne mogą mieć wartość NULL, nie zawsze możesz założyć, że NULL jest możliwym wynikiem?
Tommy Carlier
4
Jeśli funkcja zwraca wskaźnik (C / C ++) lub odwołanie do obiektu (Java), zawsze zakładam, że może zwrócić wartość null.
Giorgio
7
„Problem z używaniem wartości NULL polega na tym, że osoba korzystająca z interfejsu nie wie, czy NULL jest możliwym wynikiem i czy musi to sprawdzić, [...]” Jeśli metoda może zwrócić wartość NULL, powinna być jawnie wymienione jako część umowy API.
Zsolt Török
28

Możesz wypróbować wzór specjalnej skrzynki Martina Fowlera z Paterns of Enterprise Application Architecture :

Wartości zerowe są niewygodne w programach obiektowych, ponieważ pokonują polimorfizm. Zwykle możesz swobodnie wywoływać foo w odniesieniu do zmiennej danego typu, nie martwiąc się o to, czy przedmiot jest dokładnym typem, czy podklasą. W przypadku silnie napisanego języka kompilator może nawet sprawdzić, czy wywołanie jest prawidłowe. Ponieważ jednak zmienna może zawierać wartość NULL, możesz natknąć się na błąd środowiska wykonawczego, wywołując komunikat o wartości NULL, co da ci ładny, przyjazny ślad stosu.

Jeśli możliwe jest, że zmienna ma wartość NULL, musisz pamiętać, aby otoczyć ją testowym kodem NULL, abyś zrobił właściwą rzecz, jeśli null jest obecny. Często właściwa rzecz jest taka sama w wielu kontekstach, więc kończy się pisanie podobnego kodu w wielu miejscach - popełniając grzech powielania kodu.

Wartości zerowe są częstym przykładem takich problemów, a inne pojawiają się regularnie. W systemach liczbowych masz do czynienia z nieskończonością, która ma specjalne zasady dotyczące rzeczy takich jak dodawanie, które łamią zwykłe niezmienniki liczb rzeczywistych. Jednym z moich najwcześniejszych doświadczeń z oprogramowaniem biznesowym był klient, który nie był w pełni znany, nazywany „okupantem”. Wszystko to oznacza zmianę typowego zachowania tego typu.

Zamiast zwracać wartość null lub jakąś nieparzystą wartość, zwracaj przypadek specjalny, który ma ten sam interfejs, czego oczekuje rozmówca.

Thomas Owens
źródło
Natrafiłem na ten scenariusz, ale dopiero po wydaniu oprogramowania. Moje dane testowe nigdy go nie uruchomiły, więc było to trochę nieoczekiwane i wymagało debugowania. Chociaż nie rozwiązałem go za pomocą wzoru Special Case, dobrze jest o tym wiedzieć.
samis,
14

Myślałbym, ReverseString()że zwróci odwrócony ciąg i wyrzuci, IllegalArgumentExceptionjeśli zostanie przekazany w Null.

Myślę, że FindPerson()powinieneś podążać za wzorcem NullObject lub zgłosić niezaznaczony wyjątek dotyczący nie znajdowania czegoś, jeśli zawsze będziesz w stanie coś znaleźć.

Radzenie sobie z Nullczymś jest czymś, czego należy unikać. Posługiwanie się Nulljęzykami zostało nazwane przez wynalazcę błędem w miliard dolarów.

Nazywam to moim błędem za miliard dolarów. Był to wynalazek referencji zerowej w 1965 roku. W tym czasie projektowałem pierwszy kompleksowy system typów dla referencji w języku obiektowym (ALGOL W).

Moim celem było upewnienie się, że każde użycie referencji powinno być całkowicie bezpieczne, a sprawdzanie wykonywane automatycznie przez kompilator. Ale nie mogłem oprzeć się pokusie wprowadzenia zerowego odwołania, po prostu dlatego, że było to tak łatwe do wdrożenia.

Doprowadziło to do niezliczonych błędów, podatności i awarii systemu, które prawdopodobnie spowodowały miliard dolarów bólu i szkód w ciągu ostatnich czterdziestu lat.

W ostatnich latach wiele analizatorów programów, takich jak PREfix i PREfast w Microsoft, było używanych do sprawdzania referencji i ostrzegania, jeśli istnieje ryzyko, że mogą one nie mieć wartości zerowej. Nowsze języki programowania, takie jak Spec #, wprowadziły deklaracje dla odwołań niepustych. To jest rozwiązanie, które odrzuciłem w 1965 roku.

Tony Hoare


źródło
11

Zwróć opcję . Wszystkie zalety zwracania innej niepoprawnej wartości (np. Możliwość posiadania pustych wartości w twoich kolekcjach) bez żadnego ryzyka NullPointerException.

hugomg
źródło
1
Ciekawy. Jak zaimplementować opcję w Javie? A w C ++?
Giorgio
1
@Giorgio, W przypadku Javy biblioteki Guava od Google mają klasę o nazwie Opcjonalne .
Otavio Macedo
1
Dla C ++ jestboost::optional<T>
MSalters
8

Widzę obie strony tego argumentu i zdaję sobie sprawę, że niektóre dość wpływowe głosy (np. Fowler) opowiadają się za nie zwracaniem wartości zerowych w celu utrzymania kodu w czystości, unikania dodatkowych bloków obsługi błędów itp.

Mam jednak tendencję do popierania zwolenników powrotu wartości zerowej. Uważam, że istnieje istotna różnica w wywoływaniu metody, która odpowiada : nie mam żadnych danych i odpowiada : mam ten pusty ciąg .

Ponieważ widziałem niektóre dyskusje odnoszące się do klasy Person, rozważ scenariusz, w którym próbujesz wyszukać instancję klasy. Jeśli przekażesz jakiś atrybut wyszukiwarki (np. Identyfikator), klient może natychmiast sprawdzić wartość null, aby sprawdzić, czy nie znaleziono żadnej wartości. Niekoniecznie jest to wyjątkowe (stąd nie wymagają wyjątków), ale należy je również wyraźnie udokumentować. Tak, wymaga to pewnego rygoru ze strony klienta i nie, nie sądzę, że to wcale źle.

Teraz rozważ alternatywę, w której zwracasz prawidłowy obiekt Person ... który nie ma w nim nic. Czy wstawiasz null we wszystkich jego wartościach (nazwa, adres, ulubionyDrink), czy też zapełniasz te prawidłowe, ale puste obiekty? W jaki sposób Twój klient ustala, że ​​nie znaleziono faktycznej osoby? Czy muszą sprawdzić, czy nazwa jest pustym ciągiem zamiast null? Czy tego rodzaju rzeczy nie doprowadzą do tak dużo lub więcej bałaganu kodu i instrukcji warunkowych, niż gdybyśmy po prostu sprawdzili, czy nie ma wartości, i ruszyliśmy dalej?

Znów są punkty po obu stronach tego argumentu, z którymi mógłbym się zgodzić, ale uważam, że jest to najbardziej sensowne dla większości ludzi (czyniąc kod łatwiejszym do utrzymania).

RonU
źródło
Różnica polega na tym, „nie FindPersonbądź GetPersonzwróci null lub wyjątek, jeśli klucz nie istnieje?” Jako użytkownik interfejsu API po prostu nie wiem i muszę sprawdzić dokumentację. Z drugiej strony bool TryGetPerson(Guid key, out Person person)nie wymaga ode mnie sprawdzania dokumentacji i wiem, że nie zgłoszono wyjątku, co stanowi raczej nietypową okoliczność. O to właśnie staram się powiedzieć w mojej odpowiedzi.
Scott Whitlock
4
Myślę, że ludzie zbyt przywiązali się do wyjątków. Wyjątki są złe i pochłaniają zasoby. Nie wspominając już o tym, że ludzie próbują wyłapać wypowiedzi bez rozwiązania problemu. Wyjątki są wyraźnym znakiem, że coś poszło zasadniczo nie tak. Na przykład odwołania zerowe zgłaszają wyjątek, ponieważ odwoływanie się do obiektów zerowych jest całkowicie błędne. Błędem jest używanie go do sprawdzania, czy dana osoba została znaleziona, czy nie.
Vladimir Kocjancic
1
@VladimirKocjancic: Całkowicie się zgadzam: wyjątki powinny być stosowane w wyjątkowych sytuacjach, takich jak błędy oprogramowania, awarie sprzętu, błędna konfiguracja systemu. Jeśli obliczasz funkcję częściową (jak findPerson), to absolutnie normalne, że nie ma wyników. Nie powinno to prowadzić do wyjątku.
Giorgio
6

Zwróć null, jeśli chcesz wiedzieć, czy element istnieje, czy nie. W przeciwnym razie zwróć oczekiwany typ danych. Jest to szczególnie prawdziwe, jeśli zwracasz listę przedmiotów. Zazwyczaj można bezpiecznie założyć, że jeśli dzwoniący chce listy, będzie chciał iterować po liście. Wiele (większość? Wszystkich?) Języków kończy się niepowodzeniem, jeśli próbujesz wykonać iterację po wartości null, ale nie podczas iteracji po liście, która jest pusta.

Frustrowanie mnie przy użyciu takiego kodu jest dla mnie frustrujące:

for thing in get_list_of_things() {
    do_something_clever(thing)
}

Nie powinienem dodawać specjalnego przypadku do sprawy, gdy nie ma żadnych rzeczy .

Bryan Oakley
źródło
7
W przypadkach, w których oczekiwana jest lista lub inna grupa wartości, zawsze wybieram zwrócenie pustej listy, właśnie z tego powodu. Domyślna czynność (iteracja po pustej liście) jest prawie zawsze właściwa, a jeśli nie, osoba dzwoniąca może wyraźnie sprawdzić, czy lista jest pusta.
TMN
5

To zależy od semantyki metody. Możesz określić, że Twoja metoda akceptuje tylko „Nie null”. W Javie możesz zadeklarować, że używając adnotacji metadanych:

string ReverseString(@NotNull String stringToReverse)

Powinieneś jakoś określić wartość zwracaną. Jeśli zadeklarujesz, akceptujesz tylko wartości NotNull, jesteś zobowiązany do zwrócenia pustego ciągu (jeśli wejściowy również był pusty).

Drugi przypadek jest nieco skomplikowany pod względem semantyki. Jeśli ta metoda ma zwrócić jakąś osobę według jej klucza podstawowego (i oczekuje się, że ta osoba istnieje), lepszym sposobem jest zgłoszenie wyjątku.

Person FindPerson(int personID)

Jeśli szukasz osoby według jakiegoś odgadłego identyfikatora (co wydaje mi się dziwne), lepiej zadeklaruj tę metodę jako @Nullable.

Martin Vejmelka
źródło
3

Gdy tylko jest to możliwe, polecam stosowanie wzorca Null Object. Upraszcza kod podczas wywoływania metod i nie można czytać brzydkiego kodu, takiego jak

if (someObject != null && someObject.someMethod () != whateverValue)

Na przykład, jeśli metoda zwraca kolekcję obiektów, które pasują do określonego wzorca, wówczas zwrócenie pustej kolekcji ma większy sens niż powrót null, a iteracja po tej pustej kolekcji nie będzie miała praktycznie żadnego wpływu na wydajność. Innym przypadkiem byłaby metoda, która zwraca instancję klasy używaną do rejestrowania danych, zwracając raczej obiekt Null, niż nulljest moim zdaniem preferowana, ponieważ nie zmusza użytkowników do zawsze sprawdzania, czy zwrócone odwołanie jest null.

W przypadkach, w których zwracanie nullma sens ( findPerson ()na przykład wywołanie metody), postaram się przynajmniej podać metodę, która zwraca, jeśli obiekt jest obecny ( personExists (int personId)na przykład), innym przykładem może być containsKey ()metoda Mapw Javie). Czyści kod dzwoniącego, ponieważ łatwo można zauważyć, że istnieje możliwość, że żądany obiekt może nie być dostępny (osoba nie istnieje, klucz nie jest obecny na mapie). nullMoim zdaniem ciągłe sprawdzanie, czy odwołanie zaciemnia kod.

Laf
źródło
3

wartość null jest najlepszym rozwiązaniem, jeśli jest zwracana tylko wtedy, gdy spełnione są następujące warunki:

  • przy normalnym działaniu oczekiwany jest wynik zerowy . Można oczekiwać, że w niektórych rozsądnych okolicznościach nie będziesz w stanie znaleźć osoby, więc zwracanie wartości null przez findPerson () jest w porządku. Jeśli jednak jest to naprawdę nieoczekiwana awaria (np. W funkcji o nazwie saveMyImportantData ()), powinieneś zgłosić wyjątek. W nazwie jest podpowiedź - wyjątki dotyczą wyjątkowych okoliczności!
  • null oznacza „nie znaleziono / brak wartości” . Jeśli masz na myśli coś innego, zwróć coś innego! Dobrym przykładem są operacje zmiennoprzecinkowe zwracające nieskończoność lub NaN - są to wartości o określonych znaczeniach, więc byłoby bardzo mylące, gdybyś zwrócił tutaj wartość null.
  • Funkcja ma zwrócić jedną wartość , na przykład findPerson (). Jeśli został zaprojektowany, aby zwrócić kolekcję, np. FindAllOldPeople (), wówczas najlepsza jest pusta kolekcja. Następstwem tego jest to, że funkcja, która zwraca kolekcję nigdy nie powinny zwróci null .

Ponadto upewnij się, że udokumentujesz fakt, że funkcja może zwrócić wartość null.

Jeśli zastosujesz się do tych zasad, wartości zerowe są w większości nieszkodliwe. Zauważ, że jeśli zapomnisz sprawdzić, czy nie ma wartości null, zwykle natychmiast otrzymasz NullPointerException, co zwykle jest dość łatwym błędem do naprawienia. To szybkie podejście do awarii jest znacznie lepsze niż posiadanie fałszywej wartości zwracanej (np. Pusty ciąg), która jest cicho propagowana w twoim systemie, prawdopodobnie uszkadzając dane, bez zgłaszania wyjątku.

Wreszcie, jeśli zastosujesz te reguły do ​​dwóch funkcji wymienionych w pytaniu:

  • FindPerson - byłoby właściwe, aby ta funkcja zwróciła null, jeśli dana osoba nie została znaleziona
  • ReverseString - Wygląda na to, że nigdy nie uda się znaleźć wynik, jeśli przeszły ciąg (ponieważ wszystkie łańcuchy mogą być odwrócone, w tym ciąg pusty). Dlatego nigdy nie powinien zwracać wartości zerowej. Jeśli coś pójdzie nie tak (z pamięci?), Powinien zgłosić wyjątek.
mikera
źródło
1

Moim zdaniem istnieje różnica między zwracaniem wartości NULL, zwracaniem jakiegoś pustego wyniku (np. Pustego ciągu lub pustej listy) a zgłaszaniem wyjątku.

Zwykle stosuję następujące podejście. Uważam wywołanie funkcji lub metody f (v1, ..., vn) za zastosowanie funkcji

f : S x T1 x ... x Tn -> T

gdzie S it „stan świata” T1,…, Tn są typami parametrów wejściowych, a T to typ zwracany.

Najpierw próbuję zdefiniować tę funkcję. Jeśli funkcja jest częściowa (tzn. Istnieją pewne wartości wejściowe, dla których nie jest zdefiniowana) zwracam NULL, aby to zasygnalizować. Jest tak, ponieważ chcę, aby obliczenia zakończyły się normalnie i powiedziały mi, że żądana funkcja nie jest zdefiniowana na danych wejściowych. Używanie np. Pustego ciągu jako wartości zwracanej jest niejednoznaczne, ponieważ może się zdarzyć, że funkcja jest zdefiniowana na wejściach, a pusty ciąg jest poprawnym wynikiem.

Myślę, że dodatkowe sprawdzenie wskaźnika NULL w kodzie wywołującym jest konieczne, ponieważ stosujesz funkcję częściową, a zadaniem wywoływanej metody jest sprawdzenie , czy funkcja nie jest zdefiniowana dla podanego wejścia.

Wolę używać wyjątków dla błędów, które nie pozwalają na przeprowadzenie obliczeń (tzn. Nie było możliwe znalezienie żadnej odpowiedzi).

Załóżmy na przykład, że mam klienta klasy i chcę zaimplementować metodę

Customer findCustomer(String customerCode)

aby wyszukać klienta w bazie danych aplikacji według jego kodu. W tej metodzie zrobiłbym to

  • Zwróć obiekt klasy Customer, jeśli zapytanie zakończy się powodzeniem,
  • Zwróć null, jeśli zapytanie nie znajdzie żadnego klienta.
  • Zgłaszaj wyjątek, jeśli nie można połączyć się z bazą danych.

Dodatkowe kontrole zerowe, np

Customer customer = findCustomer("...");
if (customer != null && customer.getOrders() > 0)
{
    ...
}

są częścią semantyki tego, co robię i nie chciałbym ich po prostu „pomijać”, aby poprawić odczyt kodu. Nie sądzę, że dobrą praktyką jest upraszczanie semantyki problemu, aby uprościć kod.

Oczywiście, ponieważ sprawdzanie wartości null występuje bardzo często, dobrze jest, jeśli język obsługuje dla niego specjalną składnię.

Zastanowiłbym się również nad użyciem wzorca Null Object (jak sugeruje Laf), o ile mogę odróżnić obiekt null klasy od wszystkich innych obiektów.

Giorgio
źródło
0

Metody zwracające kolekcje powinny zwracać puste, ale inne mogą zwracać wartość null, ponieważ w przypadku kolekcji może się tak zdarzyć, że będziesz mieć obiekt, ale nie będzie w nim żadnych elementów, więc obiekt wywołujący zweryfikuje tylko rozmiar.

Phani
źródło
0

Dla mnie są tutaj dwie sprawy. Jeśli zwracasz jakąś listę, zawsze powinieneś ją zwrócić bez względu na wszystko, pustą, jeśli nie masz na niej nic do wstawienia.

Jedynym przypadkiem, w którym widzę jakąkolwiek debatę, jest zwrot jednego elementu. W takiej sytuacji wolę zwracać wartość zerową w przypadku niepowodzenia w takiej sytuacji, ponieważ powoduje to szybkie zawodzenie.

Jeśli zwrócisz obiekt zerowy, a obiekt wywołujący potrzebuje rzeczywistego obiektu, może on spróbować i użyć obiektu zerowego, powodując nieoczekiwane zachowanie. Myślę, że lepiej jest, aby rutyna zaczęła boom, jeśli zapomną poradzić sobie z sytuacją. Jeśli coś jest nie tak, jak najszybciej chcę wyjątek. Mniej prawdopodobne jest, że w ten sposób wyślesz błąd.

Loren Pechtel
źródło
0

Dodając do tego, co ludzie mówili o Maybekonstruktorze typów:

Kolejnym dużym zyskiem, poza tym, że nie ryzykuje się NPE, jest semantyczny. W świecie funkcjonalnym, w którym mamy do czynienia z funkcjami czystymi, lubimy tworzyć funkcje, które po zastosowaniu są dokładnie równoważne ich wartości zwracanej . Rozważ przypadek String.reverse(): Jest to funkcja, której dany ciąg reprezentuje odwróconą wersję tego ciągu. Wiemy, że ten ciąg istnieje, ponieważ każdy ciąg można odwrócić (ciągi są w zasadzie uporządkowanymi zestawami znaków).

A teraz co findCustomer(int id)? Ta funkcja reprezentuje klienta, który ma podany identyfikator. Jest to coś, co może istnieć lub nie. Pamiętaj jednak, że funkcje mają jedną zwracaną wartość, więc nie można tak naprawdę powiedzieć „ta funkcja zwraca klienta o podanym identyfikatorze LUB zwraca null.

To jest mój główny problem zarówno ze zwracaniem, jak nulli zwracaniem pustego obiektu. Wprowadzają w błąd. nullnie jest klientem i nie jest tak naprawdę „nieobecnością klienta”. To brak NIC. Jest to po prostu typ bez wskaźnika do NIC. Bardzo niski poziom, bardzo brzydka i podatna na błędy. Wydaje mi się, że tutaj również jest pusty obiekt. Klient zerowy JEST klientem. To nie jest brak jednego, to nie klient z prośbą o identyfikator, więc zwrot jest po prostu NIEPRAWIDŁOWY. To NIE jest klient, o którego prosić, ale nadal twierdzisz, że zwróciłeś klienta. Ma zły identyfikator, najwyraźniej jest to błąd. Łamie kontrakt sugerowany przez nazwę i podpis metody. Wymaga to, aby kod klienta zakładał pewne rzeczy o twoim projekcie lub czytał dokumentację.

Oba te problemy rozwiązuje Maybe Customer. Nagle nie mówimy „ta funkcja reprezentuje klienta o podanym identyfikatorze”. Mówimy „ta funkcja reprezentuje klienta o podanym identyfikatorze, jeśli istnieje . Jest teraz bardzo jasny i wyraźny na temat tego, co robi. Jeśli poprosisz o klienta z nieistniejącym identyfikatorem, otrzymasz Nothing. Jak to jest lepsze? niż null? Nie tylko z powodu ryzyka NPE, ale także dlatego, że rodzaj Nothingnie jest po prostu Maybe. Jest Maybe Customer. Ma znaczenie semantyczne. W szczególności oznacza „brak klienta”, co oznacza, że ​​taki klient nie istnieje.

Innym problemem z zerowym jest oczywiście to, że jest niejednoznaczny. Czy to dlatego, że nie znalazłeś klienta, czy wystąpił błąd połączenia z bazą danych, czy po prostu nie mogę go zobaczyć?

Jeśli masz kilka takich błędów do obsłużenia, możesz albo zgłosić wyjątki dla tych wersji, albo (i ja wolę w ten sposób) możesz zwrócić an Either CustomerLoadError Customer, reprezentujący coś, co poszło nie tak, lub zwrócony klient. Ma wszystkie zalety Maybe Customer, pozwalając jednocześnie określić, co może pójść nie tak.

Najważniejsze, czego szukam, to uchwycenie kontraktu funkcji w jej podpisie. Nie zakładaj rzeczy, nie polegaj na ulotnych konwencjach, bądź wyraźny.

sara
źródło
0

1) Jeśli semantyczna funkcji jest to, że nic nie może zwrócić, wywołujący MUSI ją przetestować, w przeciwnym razie np. weź swoje pieniądze i nie daj nikomu.

2) Dobrze jest tworzyć funkcje, które semantycznie zawsze coś zwracają (lub rzucają).

W przypadku programu rejestrującego warto zwrócić program rejestrujący, który nie rejestruje się, jeśli nie zdefiniowano żadnego programu rejestrującego. Zwracając kolekcję, prawie nigdy nie ma sensu (z wyjątkiem „wystarczająco niskiego poziomu”, gdzie sama kolekcja to dane, a nie to, co zawiera), aby niczego nie zwracać, ponieważ pusty zestaw jest zbiorem, a nie niczym.

Z osoba przykład pójdę „hibernacji” sposób (dostać vs obciążeniu, IIRC, ale to komplikuje obiektów proxy dla leniwego ładowania) posiadania dwie funkcje, jedną, która zwraca wartość null a po drugie, że rzuca.

użytkownik470365
źródło
-1
  • Wartość NULL powinna zostać zwrócona, jeśli aplikacja oczekuje, że dane będą dostępne, ale dane nie są dostępne. Na przykład usługa, która zwraca CITY na podstawie kodu pocztowego, powinna zwrócić wartość null, jeśli miasto nie zostanie znalezione. Dzwoniący może następnie zdecydować się na zerowanie lub wysadzenie w powietrze.

  • Pusta lista powinna zostać zwrócona, jeśli istnieją dwie możliwości. Dostępne dane lub brak dostępnych danych. Na przykład usługa, która zwraca CITY (s), jeśli populacja jest większa niż pewna liczba. Może zwrócić pustą listę, jeśli Brak danych spełniających podane kryteria.

java_mouse
źródło
-2

Returning NULL to okropny projekt w świecie zorientowanym obiektowo. Krótko mówiąc, użycie NULL prowadzi do:

  • obsługa błędów ad-hoc (zamiast wyjątków)
  • niejednoznaczny semantyczny
  • powoli zamiast szybko zawodzić
  • myślenie komputerowe a myślenie obiektowe
  • zmienne i niekompletne obiekty

Sprawdź ten post na blogu, aby uzyskać szczegółowe wyjaśnienie: http://www.yegor256.com/2014/05/13/why-null-is-bad.html

yegor256
źródło