fbpx
devstyle.pl - Blog dla każdego programisty
devstyle.pl - Blog dla każdego programisty
3 minut

Custom exceptions


09.06.2014

Kiedyś na stronach MS widziałem rekomendację mówiącą “używaj typów wyjątków dostarczanych przez framework“. Jakiś czas temu, na jakimś polskim chyba blogu, przeczytałem tego powtórkę. Wiecie co? U mnie się doskonale sprawdza kompletne przeciwieństwo tej praktyki.

Wyjątek bazowy

Zawsze w swoich aplikacjach staram się mieć jeden bazowy typ wyjątku, abstrakcyjna klasa MyAppException: Exception. Dzięki temu jestem w stanie wyłapać wszystko co rzuca mój kod, a nie jakieś flaki pod spodem. Wyjątki, które ja rzucam, będą charakterystyczne dla mojego systemu, więc będą to, jak je czasami nazywam, “wyjątki biznesowe“.

Co mi to daje? A chociażby to, że łapiąc taki wyjątek mogę zalogować wszystko co mi potrzebne, ale użytkownikowi wyświetlić taki komunikat jaki będzie dla niego zrozumiały.

I…

Dodatkowo czasem doprowadzam swoją wyjątkową praktykę do ekstremum i staram się, aby każdy wyjątek był rzucany tylko raz. Potraficie sobie to wyobrazić? Ile musi być tych klas wyjątków? Ano w tak zwaną pytę. I dobrze, dokładnie o to mi chodzi. Nie muszę wysilać się na jakieś wiele mówiące exception message. CO się stało – powie mi sam typ wyjątku. A towarzyszące temu okoliczności – czyli aktualne dane w tym obszarze systemu – przyjmowane są jako parametry konstruktora.

Na ich podstawie kleję najprostszego możliwego stringa, który wyląduje w logach. Użytkownik tego i tak nie zobaczy, bo… patrzcie wcześniej – typ wyjątku jest zmapowany na komunikat (w resx) wyświetlany użytkownikowi.

Te korzyści same w sobie są jak dla mnie wystarczające, ale to nie wszystko! Ile razy w testach sprawdzaliście, czy treść wyjątku zawiera jakieś odpowiednie informacje żeby upewnić się, że wyjątek leci z oczekiwanej ścieżki kodu? Z takiego InvalidOperationException niewiele poza tym można wyciągnąć. Moje testy znacznie się uprościły od kiedy stosuję opisywane podejście. Sprawdzam po prostu czy w odpowiednich okolicznościach leci odpowiedni typ wyjątku, to tyle.

A przykład z życia? Proszę bardzo!

Życie – dialer

Przed kilka miesięcy pracowałem nad “dialerem” – aplikacją do dzwonienia do ludzi i wciskaniu im produktów (z czegoś podobnego korzysta ten cholerny telemarketer, który w piątek wieczorem dzwoni do was z banku i próbuje przekonać że naprawdę potrzebujecie kolejnej karty kredytowej). A oto przykładowa hierarchia wyjątków (btw, chyba żadna inna część aplikacji nie ma tak rozbudowanego dziedziczenia:) ):

Najpierw mamy “abstract MyDialerException“. Potem wyjątek bazowy dla scenariusza “wykonywania połączenia”: “abstract DialingException: MyDialerException“. A poniżej już konkretne wyjątki, opisujące co poszło nie tak: CallAlreadyInProgressException : DialingException, IncorrectNumberException: DialingException, NumberAlreadyDialedException: DialingException… i jeszcze parę.

Jak ktoś zapyta “co w twoim systemie może pójść nie tak?” to wystarczy, że pokażę klasy dziedziczące z MyDialerException – i wszystko jasne.

Pozamiatane.

0 0 votes
Article Rating
24 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
trackback
9 years ago

Custom exceptions | Maciej Aniserowicz o programowaniu…

Dziękujemy za dodanie artykułu – Trackback z dotnetomaniak.pl…

pawelek
9 years ago

I super, ale jak masz jakiś argument, który wychodzi poza zakres, to tworzysz własny exception, czy nie? Bo dla biznesu nie widzę sensu robienia tego inaczej niż robisz.

Miroslaw Praglowski
9 years ago

A mi to “pachnie” sterowaniem appką za pomocą wyjątków, czy rzeczywiście scenariusz że nr jest zajęty jest aż tak wyjątkowy żeby tworzyć CallAlreadyInProgressException? IMHO to jest złe użycie wyjątków. Bardziej mi to podpada pod eventy domenowe. Używanie wyjątków do sterowania wykonaniem appki (które są kosztowne choćby przez to że trzeba call stack wyciągnąć itp) ti IMHO antypattern.

BTW sam mam w swoich apkach BusinessException – ale używam go w rzeczywiście wyjątkowych sytuacjach i tylko wtedy gdy jestem w stanie “biznesowo” obsłużyć taki wyjątek.

matma
matma
9 years ago

Z nieco innej beczki – zamiast pisać jakie masz klasy lepiej to narysować ;) http://yuml.me – jest to idealne narzędzie do tego

siararadek
siararadek
9 years ago

Tylko jak przekonać zespół, żeby zgodził się na takie podejście jak o twoich mikrokontaktach nie chce słyszeć a na propagowanie pisania tak by się łatwo mockowało jest szczególnie oporny (helpery statyczne UBER ALLES)? :D

Oskar Dudycz
9 years ago

@Mirek – też nie jestem Exception Driven Development, ale o ile dobrze zrozumiałem to Maćkowi chodzi o to, że część osób przesadza z tym, żeby tego unikać. Dla mnie osobiście rzucanie własnych wyjątków jest ok o ile (tak jak napisał Maciek powyżej) rzucamy je gdy “w teorii” sytuacja nigdy nie powinna wystąpić, a boimy się, że w praktyce (zgodnie z prawami Murphy’ego) może. Nie ma sensu np. walidować coś co nigdy nie powinno się wydarzyć. To już wtedy jest robienie “kuloodpornych aplikacji”, co również nie jest dobrą praktyką.
Oczywiście jest to dosyć śliski temat, ciężko wyważyć proporcje. Ja osobiście tworzę swoje wyjątki, gdy klient zapewnia mnie, że punktu biznesowego taka sytuacja nigdy nie wystąpi i stwierdzam, że lepiej zastosować zasadę “kontrolowanego zaufania”… ;)

Mirosław Pragłowski
9 years ago

@Orkar: po uszczegółowieniu jest to już lepiej ale i tak bym zrobił jednak inaczej (uwaga pseudokod poniżej):

class Dialer {

public void Call(…) {
if (currentStatus == CalEstablished) {
DomainEvent.CallAlreadyInProgress.Publish();
return;
}
… tu implementacja Call…
}

Efekt ten sam a nie mamy wyjątków i event możemy dowolnie przetwarzać – np. wyswietlić uzytkownikowi komunikat żeby zdjąć kubek z kawą z przycisku wywołującego fizycznie nawiązanie połączenia, zalogowanie probu połączenia do logu itp.

Sławek
9 years ago

Podejście z własnymi exception’ami jest jak najbardziej pożądane, ale ograniczył bym się do odpowienich typów wyjątków, a klasę bazową wyjątku rozszerzyłbym o pole (mógłby być enum) określający odpowieni case. W takim przypadku nie było by dodatkowych klas, lecz wystarczyła by klasa DialerException : MyApplicationException, a określenie czym błąd był spowodowany definiowało by pole z klasy bazowej. Oczywiście pozostaje kwestia konstruktorów przy Twoim podejściu gdzie prawdopodobnie przekazujesz odpowienią ilość argumentów w celu konstrukcji odpowiedniej wiadomości. W moim podejściu używam listy parametrów a konstruktor wyjątku pobiera odpowieni message z plików .resx.

Mirosław Pragłowski
9 years ago

Maciek, “zabronione przez reguły biznesowe w domenie” – czyli jak dla mnie jest to jak najbardziej część domeny. Co do sterowania flowem argument przyjąłem – spoko – po uszczegółowieniu nie mam nic przeciwko – po prostu jak dla mnie domain events dają tu większą elastyczność niż wyjątki – a i obsługa nie zawsze musi polegać na komunikacie dla usera :)

Oskar Dudycz
9 years ago

Wydaje mi się, że tak naprawdę wszyscy mówimy o tym samym i się zgadzamy, że sterowanie wyjątkami jest złe, o ile można to trzeba to właśnie zastępować takimi rozwiązaniami jak podał Mirek.
Ale wg mnie fajnie, że Maciek poruszyłeś ten temat, bo sam widzę często, że ludzie boją się używać wyjąśtków. Dużo osób próbuje robić kuloodporne aplikacje, wstawiać ify, dodawać nadmierne walidacje do sytuacji, które tak naprawdę powinny być obsługiwane wyjątkami. Zaśmieca to kod i utrudnia potem refactoring przy zmianie wymagań. Wyjątki od tego są, żeby je rzucać w sytuacji gdy następuje zdarzenie, które nigdy nie powinno wystąpić. Od tego są one… ;)

Mirosław Pragłowski
9 years ago

Tak – IMHO różnimy się tylko definicją sytuacji wyjątkowej :)

Mirosław Pragłowski
9 years ago

Maciek, sure :) Tutaj to ja “question authority” – Twoje ;) A co do definicji to jak zawsze “it depends” :) Sam w wielu apkach tak wykorzystywałem wyjątki i nie jest to zła praktyka (dopóki nie wpadniesz w control flow) ale teraz Event Sourcing itp i wszędzie eventy widzę :)

Andrzej Krzywda
9 years ago

Zainspirowany tym postem i komentarzami Mirka popełniłem blog post u siebie (uwaga: Ruby)

http://andrzejonsoftware.blogspot.com/2014/06/custom-exceptions-or-domain-events.html

Łukasz K.
9 years ago

Jak już jesteśmy w tym temacie, to mam pomysł na nastepnego posta – Best practices w tworzeniu custom exception. Jak Ty to widzisz? :)

Łukasz K.
9 years ago

Nie mam pytań. Dla mnie wszystko jest jasne. Myślałem bardziej o Twoich fanach czytających na codzień Twoje wypociny ;-)

marek
marek
9 years ago

A mnie ciekawi jakim to antypatterenm jest rzucanie wyjatkow w serwisach biznesowych (aplikacyjnych etc) – co takiego zlego jest w tym. W przypadku kodu gdzie dana akacje wykonujemy 2-100000 razy lub wiecej (w odpowiedzi na np jakas akcje uzytkownika) bedzie to zle/zbyt kosztowne, ale mowimy o sytuacji gdzie wyjatek pojdzie jeden raz na akcje uzytkownika – koszt pomijalny (zanim zaczniecie sie tym martwic – w 90% Waszych aplikacji znajdziecie wieksze problemy wydajnosciowe – i nie mowie ze to jest zle – raczej naturalne bo przy obecnych komputerach wydajnosc nie jest tak wazna jak kiedys). Ja osobisicie nie widze jakis szczegolnych wad wyjatkow w takiej sytuacji – moze bym je zauwazyl na rozmowie rekrutacyjnej gdybym wyczul iz druga osoba jest fanatykiem eventow ;) ale w normalnym uzytkownaniu nie widze jakis znacznych plusow ze stosowania rozwiazania zaproponowanego przez Mirka (wad tez nie – wazne aby w aplikacji stosowac jedno podejscie). Jeszcze na koniec odnosnie wyjatkow – nie chodzi mi o wyjatki, ktore nie maja prawa wystapic (blad aplikacji) – zalozmy ze uzytkownik chce przelac pieniadze z konta i podczas proby wykonania okazuje sie iz konto jest zamkniete (sytuacja jak najbardziej mozliwa).

Kurs Gita

Zaawansowany frontend

Szkolenie z Testów

Szkolenie z baz danych

Książka

Zobacz również