Kiedy używać potwierdzenia, a kiedy wyjątku

121

Przez większość czasu będę używał wyjątku, aby sprawdzić warunek w moim kodzie, zastanawiam się, kiedy jest odpowiedni moment na użycie asercji?

Na przykład,

Group group=null;
try{
    group = service().getGroup("abc");
}catch(Exception e){
    //I dont log error because I know whenever error occur mean group not found
}

if(group !=null)
{
    //do something
}

Czy możesz wskazać, jak pasuje tutaj stwierdzenie? Czy powinienem użyć asercji?

Wygląda na to, że nigdy nie używam asercji w kodzie produkcyjnym i widzę tylko asercje w testach jednostkowych. Wiem, że w większości przypadków mogę po prostu użyć wyjątku, aby sprawdzić jak powyżej, ale chcę wiedzieć, jak to zrobić „profesjonalnie”.

cometta
źródło

Odpowiedzi:

81

Asercje powinny być używane do sprawdzania czegoś, co nigdy nie powinno się wydarzyć, a wyjątek powinien być używany do sprawdzania czegoś, co może się wydarzyć.

Na przykład funkcja może podzielić przez 0, więc należy użyć wyjątku, ale można użyć asercji, aby sprawdzić, czy dysk twardy nagle znika.

Asercja zatrzymałaby działanie programu, ale wyjątek pozwoliłby programowi kontynuować działanie.

Zauważ, że if(group != null)nie jest to stwierdzenie, to tylko warunek.

Marius
źródło
3
„asercja mogłaby zostać użyta do sprawdzenia, czy dysk twardy nagle znika” - powiedziałbym, że nie jest to poprawne: dlaczego miałbyś chcieć, aby było to obsługiwane podczas programowania, ale nie w produkcji (kiedy asercje są zazwyczaj wyłączone)?
herman
71
Komentarz dotyczący dysku twardego jest błędny. Asercje służą do sprawdzania błędów w logice kodu. Nigdy, przenigdy nie używaj ich do sprawdzania czegoś, nad czym nie masz kontroli. Pamiętaj, że jeśli asercja nie powiedzie się, oznacza to, że twój kod jest zły .
Ian Goldby
1
@Marius Twój warunek można zastąpić asercją taką jak ta: assert group! = Null
IgorGanapolsky
1
Negocjowany, ponieważ przykład dysku twardego jest sprzeczny z twoją własną filozofią. „Zniknięcie” dysków twardych (z punktu widzenia kodu) może faktycznie nastąpić w rzeczywistości - nieważne jak nieprawdopodobne. Jak mówi @IanGoldby, asercje powinny zależeć wyłącznie od rzeczy kontrolowanych przez twój kod.
Vicky Chijwani
O wiele lepsza odpowiedź, jeśli opublikował ją Gregory Pakosz, przeczytaj ten post.
ormurin
169

Nie myślę (lista może być niepełna i zbyt długa, aby zmieścić się w komentarzu), powiedziałbym:

  • używaj wyjątków podczas sprawdzania parametrów przekazanych do publicznych lub chronionych metod i konstruktorów
  • używaj wyjątków podczas interakcji z użytkownikiem lub gdy oczekujesz, że kod klienta odzyska sprawność po wyjątkowej sytuacji
  • używać wyjątków, aby rozwiązać problemy, które mogą wystąpić
  • używaj asercji podczas sprawdzania warunków wstępnych, warunków końcowych i niezmienników kodu prywatnego / wewnętrznego
  • użyj asercji, aby przekazać opinię sobie lub zespołowi programistów
  • używaj asercji podczas sprawdzania rzeczy, które są bardzo mało prawdopodobne, w przeciwnym razie oznacza to, że w Twojej aplikacji jest poważny błąd
  • użyj asercji, aby stwierdzić rzeczy, o których (rzekomo) wiesz, że są prawdziwe

Innymi słowy, wyjątki dotyczą niezawodności aplikacji, podczas gdy potwierdzenia dotyczą jej poprawności.

Asercje są zaprojektowane tak, aby były tanie w pisaniu, możesz ich używać prawie wszędzie i używam tej praktycznej zasady: im bardziej stwierdzenie asercji wygląda głupio, tym jest cenniejsze i tym więcej informacji zawiera. Podczas debugowania programu, który nie zachowuje się we właściwy sposób, z pewnością sprawdzisz bardziej oczywiste możliwości niepowodzenia w oparciu o swoje doświadczenie. Następnie sprawdzisz problemy, które po prostu nie mogą się zdarzyć: właśnie wtedy asercje bardzo pomagają i oszczędzają czas.

Gregory Pakosz
źródło
53
Podoba mi się sposób, w jaki to sformułowałeś: wyjątki odnoszą się do solidności aplikacji, podczas gdy asercje dotyczą jej poprawności .
M. Dudley
Przekreśliłem
Gregory Pakosz
A jeśli zdarzy ci się szukać niestandardowej biblioteki assertów
Gregory Pakosz
26

Pamiętaj, że asercje można wyłączyć w czasie wykonywania za pomocą parametrów i są one domyślnie wyłączone , więc nie licz na nie z wyjątkiem debugowania.

Powinieneś także przeczytać artykuł Oracle o asercji, aby zobaczyć więcej przypadków, w których należy używać - lub nie używać - asercji.

chburd
źródło
Hue hue hue Zastanawiałem się, dlaczego mój kod nie działa w Eclipse, ale działa dobrze w wierszu poleceń.
Steve
15

Z reguły:

  • Używaj potwierdzeń do wewnętrznych sprawdzeń spójności, w których nie ma żadnego znaczenia, czy ktoś je wyłączy. (Zauważ, że javapolecenie domyślnie wyłącza wszystkie potwierdzenia).
  • Używaj regularnych testów do wszelkiego rodzaju kontroli, czego nie należy wyłączać. Obejmuje to kontrole obronne, które chronią przed potencjalnymi szkodami spowodowanymi przez błędy oraz wszelkie dane / żądania weryfikacji / cokolwiek dostarczonego przez użytkowników lub usługi zewnętrzne.

Poniższy kod z twojego pytania jest w złym stylu i może zawierać błędy

try {
    group = service().getGroup("abc");
} catch (Exception e) {
    //i dont log error because i know whenever error occur mean group not found
}

Problem w tym, że NIE WIESZ, że wyjątek oznacza, że ​​grupa nie została znaleziona. Możliwe jest również, że service()wywołanie zgłosiło wyjątek lub wróciło, nullco spowodowało błąd NullPointerException.

Kiedy wychwytujesz „oczekiwany” wyjątek, powinieneś wychwycić tylko ten wyjątek, którego oczekujesz. Wyłapując java.lang.Exception(a zwłaszcza nie rejestrując go), utrudniasz zdiagnozowanie / debugowanie problemu i potencjalnie pozwalasz aplikacji wyrządzić więcej szkód.

Stephen C.
źródło
4

Cóż, w firmie Microsoft zalecono, aby wyrzucić wyjątki we wszystkich interfejsach API, które udostępniasz publicznie, i używać potwierdzeń do wszelkiego rodzaju założeń dotyczących kodu, który jest wewnętrzny. To trochę luźna definicja, ale myślę, że wyznaczenie granicy zależy od każdego programisty.

Jeśli chodzi o użycie wyjątków, jak sama nazwa wskazuje, ich użycie powinno być wyjątkowe, więc dla kodu, który przedstawisz powyżej, getGrouppołączenie powinno powrócić, nulljeśli nie istnieje żadna usługa. Wyjątek powinien wystąpić tylko wtedy, gdy łącze sieciowe zepsuje się lub coś w tym stylu.

Wydaje mi się, że wniosek jest taki, że definiowanie granic asercji i wyjątków pozostawiono zespołowi programistów w przypadku każdej aplikacji.

rui
źródło
IMHO, problem z tego rodzaju zaleceniami polega na tym, że jest to w porządku, o ile granica między publiczną i prywatną częścią API jest dość ustalona. Jeśli tworzysz nowy kod, to ta granica jest często dość płynna ...
Len Holgate,
Tak, masz rację. To wskazówka, ale pod koniec dnia pozostawiono ją wrażliwości programistów. Nie sądzę, aby istniała dla nich ostateczna linia definiująca, więc myślę, że po prostu idziesz z tym, co uważasz za słuszne, czytając mnóstwo różnych kodów.
rui
3

Zgodnie z tym dokumentem http://docs.oracle.com/javase/6/docs/technotes/guides/language/assert.html#design-faq-general „Oświadczenie asertu jest odpowiednie dla niepublicznego warunku wstępnego, warunku końcowego i niezmiennej klasy sprawdzanie. Publiczne sprawdzanie warunków wstępnych powinno nadal być przeprowadzane poprzez sprawdzanie wewnątrz metod, które skutkują w szczególności udokumentowanymi wyjątkami, takimi jak IllegalArgumentException i IllegalStateException. "

Jeśli chcesz dowiedzieć się więcej o warunku wstępnym, warunku końcowym i niezmienności klas, zajrzyj do tego dokumentu: http://docs.oracle.com/javase/6/docs/technotes/guides/language/assert.html#usage-conditions . Zawiera również przykłady użycia asercji.

cesarsalgado
źródło
1

Testowanie pod kątem wartości null wyłapie tylko wartości null powodujące problemy, podczas gdy próba / catch, jaką masz, wykryje każdy błąd.

Ogólnie rzecz biorąc, try / catch jest bezpieczniejsze, ale nieco wolniejsze, i musisz uważać, aby wyłapać wszystkie rodzaje błędów, które mogą wystąpić. Więc powiedziałbym, że użyj try / catch - pewnego dnia kod getGroup może się zmienić i po prostu możesz potrzebować tej większej sieci.

Phil H.
źródło
1

Możesz korzystać z tej prostej różnicy podczas ich użytkowania. Wyjątki będą używane do sprawdzania oczekiwanych i nieoczekiwanych błędów zwanych błędami sprawdzonymi i niesprawdzonymi, podczas gdy asercja jest używana głównie do debugowania w czasie wykonywania, aby sprawdzić, czy założenia są sprawdzone, czy nie.

SBTec
źródło
1

Wyznaję, że twoje pytanie trochę mnie zdezorientowało. Gdy warunek potwierdzenia nie zostanie spełniony, zostanie zgłoszony wyjątek. Myląco nazywa się to AssertionError . Zauważ, że nie jest zaznaczone, jak (na przykład) IllegalArgumentException, który jest generowany w bardzo podobnych okolicznościach.

Więc używając asercji w Javie

  1. jest bardziej zwięzłym sposobem pisania warunku / bloku rzutów
  2. umożliwia włączanie / wyłączanie tych kontroli za pomocą parametrów maszyny JVM. Zwykle pozostawiłbym te kontrole przez cały czas, chyba że wpływają one na wydajność w czasie wykonywania lub mają podobną karę.
Brian Agnew
źródło
AssertionError jest podklasą Error, a nie RuntimeException.
Stephen C
Ach. Oczywiście. Myślałem o zaznaczeniu / niezaznaczeniu. Teraz poprawione
Brian Agnew
Wyjaśnia, czym są twierdzenia (z kontrowersyjnego punktu widzenia), ale nie wyjaśnia, kiedy dokładnie ich używać.
Karl Richter
1

Zobacz sekcję 6.1.2 (Potwierdzenia a inne kody błędów) dokumentacji firmy Sun pod następującym łączem.

http://www.oracle.com/technetwork/articles/javase/javapch06.pdf

Ten dokument zawiera najlepsze porady, jakie widziałem, kiedy używać asercji. Cytat z dokumentu:

„Dobrą praktyczną zasadą jest to, że w wyjątkowych przypadkach powinieneś używać asercji, o których chciałbyś zapomnieć. Asercja to najszybszy sposób radzenia sobie z warunkiem lub stanem, o którym nie spodziewasz się, i zapomnienia o nim radzić sobie z."

Phil
źródło
0

Niestety potwierdzenia mogą zostać wyłączone. Podczas produkcji potrzebujesz wszelkiej pomocy, jaką możesz uzyskać, tropiąc coś nieprzewidzianego, więc twierdzą, że dyskwalifikują się.

Thorbjørn Ravn Andersen
źródło