2009-09-18 8 views
8

Pochodzę z tła .NET, a teraz pracuję w Javie.Programowanie defensywne: Wytyczne w Javie

Obecnie mam duże problemy z zaprojektowaniem API w defensywny sposób przeciwko błędnym wejściom. Powiedzmy, że mam następujący kod (na tyle blisko):

public void setTokens(Node node, int newTokens) { 
    tokens.put(node, newTokens); 
} 

Jednak ten kod może nie z dwóch powodów:

  1. Użytkownik przechodzi węzeł null.
  2. Użytkownik przekazuje nieprawidłowy węzeł, tj. Jeden nie jest zawarty na wykresie.

W .NET, chciałbym rzucić ArgumentNullException (zamiast NullReferenceException!) Lub ArgumentException odpowiednio, przekazując nazwę argumentu naruszającego (node) jako string argument.

Java wydaje się nie mieć równoważnych wyjątków. Zdaję sobie sprawę, że mogę być bardziej konkretny i po prostu wyrzucić wszystko, co jest najbliższe opisania sytuacji, lub nawet napisać własną klasę wyjątków dla konkretnej sytuacji.

Czy to najlepsza praktyka? Czy istnieją klasy uniwersalne podobne do ArgumentException w .NET?

Czy w takim przypadku warto sprawdzić pod kątem null? Kod i tak się nie powiedzie, a ślad stosu wyjątku będzie zawierał powyższe wywołanie metody. Sprawdzenie przed null wydaje się zbyteczne i nadmierne. To prawda, że ​​ślad stosu będzie nieco czystszy (ponieważ jego celem jest powyższa metoda, a nie wewnętrzna kontrola implementacji środowiska JRE). Ale to musi być skompensowane kosztem dodatkowego oświadczenia if, które ponadto powinno być nigdy nie występuje - przecież przekazanie null do powyższej metody nie jest oczekiwaną sytuacją, jest to raczej głupi błąd. Oczekiwanie, że jest to wręcz paranoidalne - i zakończy się niepowodzeniem z tym samym wyjątkiem, nawet jeśli nie sprawdzę.

[Jak podkreślono w komentarzach, HashMap.put faktycznie zezwala na wartości null dla klucza. Więc sprawdzić przed null nie byłoby to zbędne tutaj]

+2

Wywołanie "setTokens (null, 0)" spowoduje tylko "NullPointerException", jeśli używasz 'HashTable' lub' ConcurrentHashMap', ponieważ nie pozwalają one na klucze 'null'. "HashMap" z drugiej strony cieszy się, że ma puste klucze. – pjp

+0

@pjp: Dzięki za korektę - spodziewałem się, że to się nie powiedzie, ponieważ 'HashMap' musi stworzyć mieszankę swojego argumentu. Jest jeszcze jeden powód do wyraźnego sprawdzenia. –

Odpowiedz

7

Różne grupy mają różne standardy.

Po pierwsze, zakładam, że znasz różnicę między RuntimeException s (niezaznaczone) i normalnym Exception s (zaznaczone), jeśli nie, to zobacz this question and the answers. Jeśli napiszesz własny wyjątek, możesz go zmusić do przechwycenia, podczas gdy oba NullPointerException i IllegalArgumentException są wyjątkami RuntimeException, które są w niektórych kręgach źle widziane.

Po drugie, tak jak z wami, z grupami, z którymi pracowałem, ale nie używam aktywnie twierdzeń, ale jeśli wasz zespół (lub konsument API) zdecydował, że użyje on twierdzeń, to twierdzą, że brzmi dokładnie jak właściwy mechanizm .

Gdybym był tobą, użyłbym NullPointerException. Powodem tego jest precedens. Zrób przykładowy interfejs API języka Java firmy Sun, na przykład java.util.TreeSet. Wykorzystuje NPE do tego rodzaju sytuacji i chociaż wygląda na to, że twój kod właśnie użył wartości zerowej, jest całkowicie odpowiedni.

Jak powiedzieli inni: IllegalArgumentException jest opcją, ale myślę, że NullPointerException jest bardziej komunikatywny.

Jeśli ten interfejs API został zaprojektowany do użytku przez zewnętrzne firmy/zespoły, trzymałbym się z numerem NullPointerException, ale upewnij się, że jest on zadeklarowany w javadoc. Jeśli jest to do użytku wewnętrznego, możesz zdecydować, że dodanie własnej heksarchii wyjątków jest opłacalne, ale osobiście uważam, że interfejsy API, które dodają wyjątki heirarchii wyjątków, które zostaną tylko zarejestrowane, są tylko stratą wysiłku.

Pod koniec dnia najważniejsze jest to, że Twój kod komunikuje się wyraźnie. Lokalna hierarchia wyjątków jest jak lokalny żargon - dodaje informacji dla wewnętrznych, ale może zbić z tropu osoby postronne.

Jeśli chodzi o sprawdzanie przy wartości zerowej, twierdzę, że ma to sens. Po pierwsze, pozwala na dodanie komunikatu o wartości null (np. Węzeł lub tokeny) podczas konstruowania wyjątku, który byłby pomocny. Po drugie, w przyszłości możesz użyć implementacji Map, która umożliwia null, a następnie stracisz kontrolę błędu. Koszt to prawie nic, więc jeśli profiler nie powie, że jest to problem z wewnętrzną pętlą, nie martwię się o to.

+0

Dzięki, to było naprawdę pouczające. Jeśli chodzi o sprawdzone i niezaznaczone wyjątki: naruszenie powyższych niezmienników to * błąd * w kodzie, a nie potencjalnie możliwy warunek. Używanie sprawdzonych wyjątków tutaj byłoby całkiem szalone - równie dobrze może uczynić 'NullPointerException' sprawdzonym wyjątkiem i umieścić blok' try' wokół każdego wywołania metody. –

+0

Opisowy _name_ dla wyjątku może być bardzo pomocny, gdy stacktrace jest jedyną wskazówką dotyczącą problemu. –

+0

@ Thorbjørn Zwykle wolę dobre wiadomości od lepiej wymienianych wyjątków, chociaż jedno i drugie jest użyteczne. –

7

W Javie normalnie rzucać IllegalArgumentException

+2

BTW. Używamy Jakarta Commons-Lang do sprawdzania stylu umowy. E.g. Validate.notNull (węzeł) lub Validate.isTrue (node.isEmpty()) Te IllegalArgumentExceptions rzucać dla ciebie – Fortyrunner

+1

Dla niektórych wartości "normalnie". Sądzę, że wiele standardowych interfejsów Java API wyśle ​​NPE zamiast IAE po przekazaniu parametru null. –

2

swoje podejście zależy wyłącznie od tego, co umowa czynność oferuje rozmówców. - jest to warunkiem, że węzeł jest Nie jest zerem?

Jeśli tak, powinieneś wyrzucić wyjątek, jeśli węzeł jest pusty, ponieważ jest to naruszenie umowy. Jeśli tak nie jest, twoja funkcja powinna po cichu obsługiwać węzeł zerowy i odpowiednio reagować.

12

Standardowy wyjątek Java to IllegalArgumentException. Niektórzy rzucają NullPointerException, jeśli argument jest zerowy, ale dla mnie NPE ma konotację "ktoś spieprzył" i nie chcesz, aby klienci twojego API myśleli, że nie wiesz, co robisz.

W przypadku publicznych interfejsów API sprawdź argumenty i wcześnie i bezbłędnie prześlij. Czas/koszt ledwie ma znaczenie.

+1

+1 dla argumentu IAE a NPE. Zakładam (słusznie lub niesłusznie), że NPE jest nieprzewidzianym błędem, podczas gdy IAE (z odpowiednią wiadomością) następuje po wyraźnym sprawdzeniu zerowym. –

2

Jeśli potrzebujesz przewodnika, jak napisać dobry kod Java, mogę bardzo polecić książkę Effective Java autorstwa Joshua Blocha.

+0

Ta książka jest już na mojej liście życzeń, ale obecnie mam dużo ponad mój budżet na książki . :-( –

+2

Najlepsza książka Javy w historii Prawdopodobnie najlepsza książka programowania w historii –

+0

Zgadzam się z don. Jeśli dużo pracujesz z Javą, ta książka powinna mieć absolutny najwyższy priorytet w stosunku do czegokolwiek na twojej liście życzeń. o rzeczach, o których mówisz w pytaniu: – Jonik

0

jak inne: java.lang.IllegalArgumentException. O sprawdzaniu węzła pustego, a co z sprawdzaniem błędnego wejścia przy tworzeniu węzła?

2

Brzmi to jak to może być odpowiednie zastosowanie dla assert:

public void setTokens(Node node, int newTokens) { 
    assert node != null; 
    tokens.put(node, newTokens); 
} 
+0

Tak, czy ktokolwiek faktycznie kompiluje się z włączonymi asercjami? Myślałem, że są one bardzo nie na miejscu i zostały prawie zastąpione testami jednostkowymi i innymi technikami –

+0

Oczywiście, używam potwierdzeń do sprawdzania stanu wewnętrznego Ważne jest to, że można je włączyć w terenie, więc jeśli program zachowuje się dziwnie, masz je uruchamiane z włączonymi potwierdzeniami i może Ci powiedzieć coś użytecznego o tym, jak aplikacja jest faktycznie używana, co może nie mieć został objęty testem: Ale dla publicznego interfejsu API, ponieważ zamierzasz go przetestować jawnie, tak jak sert jest zbędny. – Ken

+0

Sprawdź komentarz pjp do odpowiedzi skaffmana. –

1

Myślę, że wiele zależy od umowy o sposobie i jak dobrze rozmówca jest znany.

W pewnym momencie w procesie dzwoniący mógł podjąć działania, aby potwierdzić węzeł przed wywołaniem metody. Jeśli znasz dzwoniącego i wiesz, że te węzły są zawsze sprawdzane, to myślę, że można założyć, że uzyskasz dobre dane. Zasadniczo odpowiedzialność spoczywa na dzwoniącym.

Jeśli jednak, na przykład, udostępniasz dystrybuowaną bibliotekę strony trzeciej, musisz sprawdzić poprawność węzła pod kątem wartości null itp. ...

Wyjątek illegalArugementException jest standardem java, ale jest także wyjątkiem RunTimeException. Więc jeśli chcesz zmusić wywołującego do obsługi wyjątku, musisz podać wyjątek check, prawdopodobnie niestandardowy, który utworzysz.

1

Osobiście chciałbym, aby wyjątki NullPointerException występowały WYŁĄCZNIE przez przypadek, więc należy użyć czegoś innego, aby wskazać, że została przekazana niedozwolona wartość argumentu. IllegalArgumentException jest w porządku.

if (arg1 == null) { 
throw new IllegalArgumentException("arg1 == null"); 
} 

ta powinna być wystarczająca, aby zarówno ci czytania kodu, ale także ubogich dusza, która dostaje wezwanie pomocy o 3 rano.

(i zawsze zapewniają tekst objaśniający dla wyjątków, z pewnością docenią im trochę smutny dzień)

0

nie mam nikogo do pomocy, więc co mam teraz zrobić jak kod kanonicznej jest

void method(String s) 

if((s != null) && (s instanceof String) && (s.length() > 0x0000)) 
{ 

co daje mi dużo snu.

Inni nie zgodzą się.

+2

Nie jestem pewien, czy musisz wykonać sprawdzenie instancji.Sygnatura metody wymaga czegoś, co jest ciągiem lub podklasą jednego, ale ciąg jest ostateczny. –

+0

@Platinum Azure: Problem z kompilatorem powinien być sprawdzany podczas kompilacji, ale widziałem, gdzie można uzyskać wyjątek klasy rzucania w czasie wykonywania, więc po prostu go tam umieściłem. Wydaje się działać lepiej. Będą ci, którzy się nie zgadzają. btw, zrobiłem kilka testów na łańcuchu i wydaje się, że tworzę kopię char [], więc char [0] ++ nie ma żadnego efektu ... każdy duży sklep z cenną własnością na linii napisze własne kompilatory, aby uniknąć unkonwn w takich kwestiach. Powinien być standardowym poziomem maserów cs. –

+0

z '' instanceof String', nie ma potrzeby dodatkowego sprawdzania 'null' ... –