Obsługa wyjątków – najlepsze praktyki

Try-to-catch-meJestem w trakcie lektury świetnej książki Roberta C. Martina – „Clean code”. Przez wielu uważana za jedną z najlepszych w tej dziedzinie. Jest to pozycja obowiązkowa w bibliotece każdego programisty.

Do tej pory sięgałem do niej (jak i do innych książek) dosyć wyrywkowo, ale sytuacja się  zmieniła gdy kupiłem ostatnio czytnik e-booków – Kindle Touch 2014 (za 319 zł. w Komputroniku). Zacząłem więcej czytać, znacznie więcej niż do tej pory i do tego w miejscach w których nigdy tego wcześniej nie robiłem (czekając w samochodzie, w kolejce do lekarza, itp.). Zdecydowanie mój numer jeden wśród podręcznych gadżetów.

Przez kilka dni przeczytałem już prawie dwie książki: „Pełna moc możliwości” J. Walkiewicza (który swoim wykładem na YT w zasadzie nakłonił mnie do założenia tego bloga), i kończę właśnie Clean code w języku angielskim. W kolejce czeka jeszcze kilka pozycji z kombinatoryki i algorytmów. W Kindlu świetne jest to, że kupuję ebooka w jednym z polskich sklepów internetowych, włączam WiFi w czytniku i książka po chwili jest w urządzeniu. Sklep wysyła ebooka na specjalnego maila w Amazonie, a Amazon wysyła go do mojego urządzenia. Świetna sprawa.

O „czystym kodzie” napiszę trochę więcej w przyszłości. Autor w swoim bestsellerze traktuje między innymi o dobrych praktykach obsługi wyjątków na temat których również chciałbym się podzielić informacjami.

Lista dobrych praktyk obsługi wyjątków:

 

  1. Wykorzystuj wyjątki zamiast zwracania kodów błędu.
    Nie zwracaj kodów błędu w postaci numeru, enuma czy nawet stringa.
  2. Napisz blok try-catch-finally w pierwszej kolejności w funkcji która może zwrócić wyjątek.

  3. Nigdy nie zwracaj wartości null.
    Jeśli kod będzie eliminował możliwość powstania wyjątku NullPointerException, będzie on czytelniejszy.
  4. Nigdy nie przekazuj do metody wartości null.
    Zwracanie wartości null jest złe, ale przekazywanie wartości null do funkcji jest jeszcze gorsze.
  5. Nigdy nie porzucaj informacji o wyjątku w bloku catch.

    Zwracając „null” zamiast obsłużenia wyjątku lub ponownego jego wyrzucenia tracisz powód jego wystąpienia. Nigdy tego nie rób!
  6. Deklaruj jak najbardziej szczegółowe kontrolowane wyjątki, które twoja metoda może wyrzucić.

    Jeśli metoda może wyrzucić zbyt wiele kontrolowanych wyjątków, należy je opakować we własnym nowo utworzonym umieszczając w komunikacie szczegóły.
  7. Nie przechwytuj klasy Exception zamiast konkretnej podklasy.

    Problem tu jest taki, że jeżeli w przyszłości do metody zostanie dodany kolejny zwracany wyjątek, programista nie będzie wiedział, że powinien go obsłużyć (a mógłby to zrobić np. inaczej niż dla innego wyjątku).
  8. Nigdy nie przechwytuj wyjątków klasy Throwable.
    Po Throwable dziedziczy klasa Error. Wyjątki dziedziczące po klasie Error informują o nieodwracalnych błędach, które nie mogą być obsłużone przez JVM.
  9. Zawsze prawidłowo opakowuj wyjątki we własnych wyjątkach tak, aby nie stracić informacji o źródle jego powstania.

    Stracisz tutaj stack trace źródłowego wyjątku. Prawidłowa wersja:
  10. Loguj wyjątki lub je ponownie wyrzucaj, ale nigdy jednocześnie.

    Spowodujesz tym wielokrotny zapis tej samej informacji w logach.
  11. Nigdy nie wyrzucaj wyjątku z bloku finally.
  12. Zawsze przechwytuj tylko te wyjątki, które potrafisz aktualnie obsłużyć.

    Przechwytuj wyjątki które potrafisz obsłużyć, lub gdy chcesz udostępnić szczegółowe informacje w konkretnym wyjątku. Jeśli nie potrafisz obsłużyć wyjątku, lepiej usuń jego przechwycenie w bloku catch i pozwól  metodzie nie wyrzucenie go.
  13. Nigdy nie używaj printStackTrace().
    Wykorzystuj loggera. Nie ma się co oszukiwać, nikt nie przegląda logów aplikacji. Wykorzystując loggera zapewniasz sobie elastyczność śledzenia błędów. Może w przyszłości chciałbyś otrzymać wiadomość e-mail informującą o pojawieniu się jakiegoś poważnego problemu? Wykorzystuj np. Log4J lub bardziej ogólnego SLF4J (który też może używać Log4J).
  14. Używaj bloków finally zamiast catch gdy nie zamierzasz obsłużyć wyjątku.

    Jeśli metoda someMethod() wywołuje metodę someMethos2() zwracającą wyjątek, którą nie chcesz obsłużyć w metodzie someMethod(), ale w dalszym ciągu chcesz zrobić porządek w przypadku pojawienia się wyjątku, zrób to w bloku finally. Nie wykorzystuj do tego bloku catch.
  15. Zapamiętaj zasadę “Throw early catch late” (wyrzucaj jak najwcześniej, przechwytuj jak najpóźniej).
    To jest najbardziej popularna reguła odnosząca się do przechwytywania wyjątków.
  16. Zawsze rób porządki po obsłużeniu wyjątku.
    Jeżeli wykorzystujesz zasoby takie jak np. połączenia do bazy danych czy połączenia sieciowe zagwarantuj ich zwolnienie w bloku finally.
  17. Wyrzucaj z metody tylko istotne wyjątki.
    Np. metoda próbująca odczytać plik wyrzucająca wyjątek NullPointerException nie przekaże użytkownikowi żadnej istotnej informacji. Zamiast tego lepszym wyjściem jest opakowanie tego wyjątku we własny, np. NoSuchFileFoundException, która w tym momencie będzie bardziej pomocna i pozwoli na podjęcie odpowiednich kroków do wyeliminowania problemu.
  18. Nigdy nie używaj wyjątków do obsługi kontroli przepływu w programie.
    Powoduje to, że kod jest ciężki do czytania i niezrozumiały.
  19. Waliduj dane pochodzące od użytkownika aby wyłapać błędne warunki na jak najwcześniejszym etapie przetwarzania żądania.
    Zawsze weryfikuj dane pochodzące od użytkownika w najwcześniejszym etapie. Pozwoli to zminimalizować kod obsługi wyjątków w logice programu.
    Na przykład – jeśli logika rejestracji użytkownika wygląda tak:
    1) Rozpoczęcie transakcji
    2) Walidacja User
    3) Zapis User
    4) Walidacja address
    5) Zapis address
    6) Jeżeli wystąpił błąd – rollback.
    Takie rozwiązanie jest nieprawidłowe. Powoduje pozostawienie bazy danych w niespójnym stanie w wielu scenariuszach.
    Lepszym rozwiązaniem jest walidacja wszystkich w jednym miejscu, a dopiero później przekazanie danych do zapisu. Prawidłowe podejście:
    1) Rozpoczęcie transakcji
    2) Walidacja User
    3) Walidacja address
    4) Zapis User
    5) Zapis address
    6) Jeżeli wystąpił błąd – rollback
  20. Zawsze umieszczaj wszystkie informacje o wyjątku w jednym komunikacie loggera.
    LOGGER.debug(“Error information A”);
    LOGGER.debug(“Error information B”);Nigdy tego nie rób. Może to spowodować niepotrzebne wykorzystywanie zasobów. Zamień to na:LOGGER.debug(“Error information A, Error information B”);
  21. Przekazuj wszystkie istotne informacje do wyjątków tak, by informowały o przyczynie ich powstania tak szczegółowo jak to tylko możliwe.
    Czyli szczegółowa informacja i stack trace.
  22. Zawsze zakończaj wątek, jeśli został przerwany (interrupted).

    Wyjątek InterruptedException informuje że powinieneś zakończyć wszystko co robisz. Zamiast powyższego kodu obsłuż go tak:
  23. Używaj metod szablonowych do powtarzających się bloków try-catch.
    Nie ma sensu używanie tego samego bloku try-catch w kilku miejscach kodu. Bloki try-catch są nieczytelne. Używaj metod szablonowych w takich przypadkach. Na przykład poniższy kod próbuje zamknąć połączenie do bazy danych.

    Taka składnia może być wykorzystywana w tysiącach miejsc w kodzie twojego programu. Lepiej zdefiniuj metodę i używaj jej wszędzie:

     

 

To również może Cię zainteresować:

  • Blog, rozwój, zmiany, ŚwiętaBlog, rozwój, zmiany, Święta Trochę ponad miesiąc temu postanowiłem założyć bloga - swego rodzaju osobisty notatnik programisty. To była szybka piłka, od momentu powstania pomysłu, stronę udostępniłem po trzech […]
  • Mapowanie XML obiektów Java – JAXBMapowanie XML obiektów Java – JAXB Podczas tworzenia oprogramowania na każdym kroku mamy kontakt z danymi w formacie XML. Opiszę tutaj jeden ze sposobów konwersji danych w obie strony Java <-> XML. Ale na […]
  • 10-letni chłopak zdobył 100% na egzaminie OCPJP z Javy!10-letni chłopak zdobył 100% na egzaminie OCPJP z Javy! W zeszłym roku Ronil Shah zaskoczył prawie wszystkich zdobywając sto procent punktów w egzaminie z Javy OCPJP. Jednak to nie wszystko. Najciekawsze jest to, że egzamin ten przewidziany […]
  • HTML, CSS 2.1 i dokumenty PDFHTML, CSS 2.1 i dokumenty PDF W ostatnim czasie realizowałem projekt w którym należało zaprojektować ok. sto dokumentów PDF w postaci różnego rodzaju wniosków, w różnym układzie. Ten, kto kiedykolwiek generował […]
  • Programowanie funkcyjne w Javie.Programowanie funkcyjne w Javie. Co nieco o programowaniu funkcyjnym. Czy możliwe jest całkowite usunięcie ze swojego kodu wyrażeń typu if / else, for, while, do while? Wydaje się to niemożliwe, bo wielu programistów […]
  • Koniec z appletami JavyKoniec z appletami Javy Pod koniec zeszłego roku wielu producentów przeglądarek usunęło lub zaplanowało usunięcie wsparcia dla wtyczek NPAPI. NPAPI to API do tworzenia wtyczek w przeglądarkach internetowych, […]

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *