W każdym programie większym niż Hello World istnieje możliwość wystąpienia błędów w czasie działania. Dotyczy to zwłaszcza takich, które nie są zależne od programisty piszącego kod aplikacji, lecz na przykład od danych zewnętrznych pochodzących od użytkownika czy z plików.
W zależności od typu i stopnia dolegliwości błędy mogą być obsługiwane na różne sposoby. Dość często nie jest wcale łatwo zdecydować się na któryś z nich. Dlatego należy znać typowe metody sygnalizowania błędów i przynajmniej ogólne zasady opisujące sytuacje, w których każda z tych metod jest najwłaściwsza.
Sam przez bardzo długi czas miałem na przykład pewne wątpliwości co do przydatności asercji jako mechanizmu powiadamiania o błędach. W szczególności, nie potrafiłem odróżnić sytuacji, w których właściwsze jest korzystanie właśnie z asercji niż chociażby z wyjątków. Ostatnio aczkolwiek całkiem przypadkowo nadrobiłem te niechlubne zaległości :)
Okazuje się bowiem, że różnica między asercją a wyjątkiem jest znaczna. Asercje służą do sprawdzania pewnych warunków, które uznajemy za obiektywnie prawdziwe. Są to po prostu założenia, które muszą być spełnione w każdym okolicznościach, gdyż warunkują poprawność kodu. Pisanie asercji jest więc częściowo sprawdzaniem samego siebie: dzięki nim łatwiej wykryjemy błędy programistyczne we wczesnej fazie powstawania kodu (czyniąc naturalnie optymistyczne założenie, że same asercje są w porządku :]).
Wniosek z tego taki, że w dobrze działającej aplikacji asercje zawsze powinny być spełnione i nigdy nie przerywać działania programu. W przeciwieństwie do nich wyjątki (exceptions) mogą pojawiać się w zupełnie poprawnym kodzie, bo dotyczą rzeczy, na które program nie ma wpływu. Jednocześnie jednak musi on przewidywać ich ewentualne wystąpienie i posiadać odpowiedni kod ich obsługi. W przeciwnym razie konsekwencje bywają nieprzyjemne.
Dlatego wyjątki (i podobne do nich mechanizmy, jak np. symbianowe leaves) służą do powiadamiania o sytuacjach mających potencjalnie poważne konsekwencje. Dla pozostałych należy stosować mniej radykalne metody informowania o błędach. Najpopularniejszym jest oczywiście wartość zwracana przez funkcję, w której wystąpił błąd (ewentualnie w połączeniu z czymś takim jak errno
lub GetLastError
w Windows API). Kluczową cechą tego sposobu powiadamiania jest to, że należy specjalnie zadbać o sprawdzenie, czy błąd wystąpił; inaczej zostanie on zignorowany.
Tak w skrócie przedstawia się sprawa od strony teoretycznej. W praktyce odróżnienie sytuacji podpadającej pod asercję od tej, gdzie uprawniony jest wyjątek – a już zwłaszcza wyjątku od zwykłego “return -1;
” – bywa trudne, a często subiektywne. Zwykle jednak daje się stosować przynajmniej jedną zasadę: przynajmniej w fazie testów lepiej jest przesadzać i reagować nazbyt histerycznie niż przeoczyć lub zignorować jakiś ważny szczegół.