2010-02-11 9 views
17

Czy lepiej jest zwrócić wartość pustą lub wyrzucić wyjątek z metody API?java api design - NULL lub Exception

Wracając null wymaga brzydkie kontrole zerowe całym i spowodować poważny problem z jakością, jeśli zwrot nie jest zaznaczone.

Wyrzucenie wyjątku wymusza na użytkowniku kodowanie błędnego stanu, ale ponieważ wyjątki w języku Java pojawiają się i zmuszają kod wywołujący do ich obsługi, ogólnie rzecz biorąc, używanie niestandardowych wyjątków może być złym pomysłem (szczególnie w java).

Każdy dźwięk i praktyczne porady?

+0

Czy znasz wzór obiektu zerowego? http://pl.wikipedia.org/wiki/Null_Object_pattern –

Odpowiedz

5

Myślę, że odpowiedź jest całkowicie zależne od kontekstu aplikacji, a co uważasz za za „wyjątkowe okoliczności” versus błędu programisty.

Na przykład, jeśli pisanie parsera XML można wybrać do wdrożenia metod takich jak:

/** 
* Returns first child element with matching element name or else 
* throws an exception. Will never return null. 
*/ 
Element getMandatoryChildElement(Element parent, String elementName) 
throws MissingElementException; 

... jak takie podejście pozwala na wdrożenie wiadomość parsowania kodu w jednym bloku logiki, bez konieczności aby sprawdzić, czy wiadomość jest poprawnie utworzona po pobraniu każdego elementu lub atrybutu.

+0

@Adamski - Podoba mi się to. Ale jeśli wywołujący już rzuca wyjątek, ten wyjątek zostałby wyciszony, prawda? Dla np. Widziałem NumberFormatException nie obsługiwane, ponieważ wywołujący już wyrzuca wyjątek. Czy istnieje zatem kolejna reguła - metoda nigdy nie powinna wyrzucać samego "wyjątku"? –

+0

Jestem zdania, że ​​jeśli elementu nie można znaleźć, osoba dzwoniąca nie powinna w ogóle wywoływać tej metody. Możesz mieć metodę sprawdzania, więc możesz napisać, jeśli (hasElement ("test")) getMandatoryChildElement ("test") ;. Znacznie lepiej niż kod typu "try-catch-try". Jak napisałbyś metodę "załaduj plik"? –

+0

@disown: problem polega na podaniu metody w interfejsie API. Nie znasz użytkownika, więc musisz upewnić się, że nie będzie robić złych rzeczy i powiedzieć mu, kiedy robi źle. @ srini.venigalla: jeśli dzwoniący już zgłasza wyjątek, wcześniej czy później go gdzieś złapie. –

1

To naprawdę zależy od tego, jak chcesz, aby ta sytuacja była obsługiwana. Jeśli rzeczywiście jest to warunek błędu, wyrzuć wyjątek. Jeśli wartość null jest akceptowalnym wyjściem, po prostu udokumentuj ją i pozwól użytkownikowi ją obsłużyć.

6

Jeśli null jest akceptowalną wartością zwrotną (tj. Może wynikać z ważnych danych wejściowych, a nie z przypadku błędu), to następnie zwraca null; w przeciwnym razie wyrzuć wyjątek.

ja w ogóle nie rozumiem swoje oświadczenie, że sprawdzone Wyjątkiem może być zły pomysł, ponieważ rozmówca jest zmuszony do ich obsługi - to cały sens sprawdzonych wyjątków. Chroni przed warunkami błędu propagującymi kod bez odpowiedniego postępowania.

+0

@danben - Naprawdę nie podoba mi się API, które wyrzuca pół tuzina niestandardowych wyjątków i muszę dodać stos nieistotnych procedur obsługi wyjątków. To samo w sobie nie stanowi problemu, ale jeśli API doda nowy wyjątek, cały łańcuch upstream musi zostać zaktualizowany. Rozumiem wszystko, co jest konieczne lub nawet dobre, ale szukałem jasnych konwencji do naśladowania. –

+1

@ srini.venigalla - pół tuzina jest na ogół zbyt wiele. Pamiętaj, że nie ma powodu, aby koniecznie mieć DOWOLNE ** niestandardowe ** wyjątki - możesz rzucić 'IllegalArgumentException' lub coś innego, co pasuje do twoich potrzeb. – danben

+0

Jaka jest korzyść z posiadania niestandardowego sprawdzanego wyjątku, takiego jak wyjątek AxisException lub SoapException lub WhateverException? Wszystko, co możesz wywnioskować z tego, to że wyjątek pochodzi z biblioteki, którą i tak znałeś ze stosu. Jeśli chcesz wykonać obsługę błędów catch-all, jest ona łatwo dodana w kliencie. –

1

Jeśli masz błędy biblioteki wewnętrznych (niespodziewane), niech wyjątki środowiska wykonawczego propagować. Jeśli w kodzie biblioteki wystąpi błąd, oczekuje się, że klient ulegnie awarii. Napraw błąd w bibliotece. Nie przechwytuj wszystkich i zwróć wyjątek specyficzny dla biblioteki, to nie przyniesie nic dobrego.

Jeśli oczekujesz rzeczy do (czasami) nie udać, zbudować to do API. Jest to oparte na zasadzie, że nie powinieneś używać wyjątków dla normalnego przebiegu programu.

Na przykład:

ProcessResult performLibraryTask(TaskSpecification ts) 

W ten sposób można mieć ProcessResult wskazują warunki błędach:

ProcessResult result = performLibraryTask(new FindSmurfsTaskSpecification(SmurfColor.BLUE)); 
if (result.failed()) { 
    throw new RuntimeException(result.error()); 
} 

Takie podejście jest podobne do zwróci null podejścia, ale można wysłać więcej informacji z powrotem do klient.

EDIT:

interakcji, które nie są zgodne z ustalonym protokołem, można rzucić błędy wykonawcze, które można udokumentować. Na przykład:

if (currentPeriod().equals(SmurfColor.BLUE) && SmurfColor.GREEN.equals(taskSpecification.getSmurfColor()) { 
    throw new IllegalStateException("Cannot search for green smurfs during blue period, invalid request"); 
} 

Należy pamiętać, że jest to spowodowane interakcją, która naruszyła umowę i nie należy się spodziewać.

+0

Podoba mi się, ale to podejście, podobnie jak powrót zerowy, błaga o miłosierdzie dzwoniącego. –

+0

Zgoda. Konieczne będzie ustalenie limitu błędów, które według Ciebie prawdopodobnie wystąpią podczas normalnego użytkowania, i modelowanie ich jako nie-wyjątków. Błędy kodowania (naruszenie umowy) lub rzeczy, których nie widzisz, dzieje się podczas normalnego użytkowania (jak brak pamięci, dereferencja pustych wskaźników), powinieneś używać wyjątków. –

2

I mają tendencję do powrotu null „getXXX” metod, które, na przykład, przynieś coś z przykładu bazie : Użytkownik getUserByName (String name) jest OK dla mnie zwróci null

Ale jeśli potrzebujesz co pobierasz, wywołując metodę, wyrzuć wyjątek: ex: getDatabaseConnection() musi zawsze zwracać połączenie i nigdy nie zwracać wartości null

W przypadku metod zwracających kolekcję lub tablicę, NIGDY nie zwraca wartości null. zwraca pustą kolekcję/tablicę zamiast:

+0

Zwykle używam findXXX dla metod, które mogą zwracać wartość null, a getXXX dla tych, którzy tego nie robią. –

+0

@disown - to byłoby niestandardowe i uniemożliwić korzystanie z bibliotek i kodu źródłowego Java. – Robin

+0

@Robin: Jeśli piszesz interfejs API bazy danych? Javabeans są dla prostych klas typu DTO. Nie nazbyt poważnie nazywasz wszystkie swoje metody zgodnie ze specyfikacją bean java? –

1

Zgadzam się z innymi, jeśli wartość null jest akceptowalną wartością zwrotu, zwróć ją, ale w przeciwnym razie nie.

To, co chciałbym dodać, to fakt, że można również odróżnić sprawdzane i niezaznaczone wyjątki. W niektórych przypadkach może być wskazane użycie niezaznaczonego wyjątku, np. użytkownik twojego API prawdopodobnie nie jest w stanie poradzić sobie z tym wyjątkiem. Spring Framework bardzo często korzysta z niezaznaczonych Wyjątków, co sprawia, że ​​kod jest często łatwiejszy do odczytania, ale czasem trudniej jest wyśledzić błędy.

6

Josh Bloch zaleca zwracanie pustych obiektów lub obiektów "unit" w miejsce wartości pustych. Sprawdź Effective Java, aby uzyskać mnóstwo przydatnych ciekawostek Java.

3

W dokumencie Practial API Design autor informuje, że nie zwraca wartości null (see p.24).

Jego głównym założeniem jest to, że prowadzi do zwiększenia liczby kontroli o wartości o wartości null i większej ilości dokumentacji.

Gorąco polecam również przeczytać "Null References: The Billion Dollar Mistake" przez organ w tej dziedzinie (T. Hoare).

+0

+1 do ciebie - Jeśli tylko ktoś może umieścić wartość wokół czasu poświęconego na rozwiązywanie problemów z null i problemów z zapamiętywaniem .. –

+0

* miliard dolarów: P – Jeriko

+0

@Jeriko masz rację. naprawiony. – ewernli

4

Chodzi mi o to, że

Nigdy nie zwróci NULL gdy zwrot typ jest zbieranie lub tablicą. Jeśli nie ma zwracanej wartości, zwróć pustą kolekcję lub pustą tablicę.

To eliminuje potrzebę zerowej kontroli.

Dobra praktyka:

public List<String> getStudentList() 
{ 
    int count = getStudetCount(); 
    List<String> studentList = new ArrayList<String>(count); 
    //Populate studentList logic 
    return studentList; 
} 

Bad Praktyka:. Nigdy nie pisz tego kodu jak poniżej.

public List<String> getStudentList() 
{ 
    int count = getStudetCount(); 
    if(count == 0) 
    { 
     return null; 
    } 
    //Populate studentList logic 
    return studentList; 
} 
+1

Jeśli 'studentList' ma być niezmienny: lepiej byłoby sprawdzić' count' zwrócony przez 'getStudentCount()', a następnie użyć 'Collections.emptyList()' jeśli był 0. To pozwala na potencjalną optymalizację (tj. posiadanie buforowanej pustej listy, która jest zwracana za każdym razem). Jeśli 'count' nie ma wartości 0, powinieneś zawinąć zwróconą listę w opakowaniu' Collections.unmodifiableList (studentList) ', aby upewnić się, że nie została przypadkowo zmodyfikowana. – cdmckay

0

Generalnie patrzę na analogiczny przykład w ramach JDK.

W przypadku akcesorów, sensowne jest użycie wartości Null.

Do wskazania brakujących lub nieznanych danych należy użyć wartości Null.

Dla wszystkich innych elementów generalnie oznacza to błąd przetwarzania.

Teraz mamy 2 opcje, czas wykonania w porównaniu do czasu kompilacji. Oto przykłady tego, jak szukam analogii.

Pozwala wyciągnąć przykład z wyjątku ArrayIndexOutOfBoundException. Dostęp lub mutacja elementu jest bardzo powszechną operacją.

jeśli zostało zaznaczone, kod konsumpcyjny będzie bardzo zagracony. Ponadto, ogólnie rzecz biorąc programiści zdają sobie sprawę z sytuacji, która może prowadzić do wyjątku ArrayIndexOutOfBounException i można tego uniknąć, pisząc dobry kod.

Po drugiej stronie, jeśli mamy kawałek kodu, taki jak otwarcie pliku, który może być spowodowany zewnętrznymi zależnościami (brak pliku lub brak wystarczających przywilejów) i nie jest zakorzeniony w błędach w programach, jest to dobry pomysł mieć sprawdzony wyjątek.