Kiedy rzucić wyjątek?

Odpowiedz Nowy wątek
2018-11-22 14:34
0

Ludzie piszą, że sterowanie przepływem za pomocą wyjątków to zło. Wydaje się to oczywiste, ale niektórzy inaczej to rozumieją: https://devstyle.pl/2016/11/2[...]tat-wykonania-komendy-w-cqrs/ Pytanie: w jakich sytuacjach warto rzucić wyjątek zamiast zwrócić obiekt rezultatu? Są w ogóle takie sytuacje?

Pozostało 580 znaków

2018-11-22 14:47
1

Bardzo dużo zależy od języka.

Jeśli język nie ma Maybe, Optional itd. i po prostu nie da się inaczej zasygnalizować, że albo będzie wynik, albo będzie śmieć, to rzucenie wyjątku pozwala nam to jakoś obejść. Jeśli język obsługuje wyjątki bezkosztowo (tzn. nie rzucenie wyjątku jest tak samo szybkie, jak kod bez wyjątków), to w kodzie krytycznym warto, jeśli testy tak wykażą, obsługiwać tak wysoce niestandardowe zdarzenia. I wreszcie kiedy nasza funkcja może się posypać z bardzo różnych powodów, a nas interesuje dokładnie z jakiego się posypała tym razem, to wyjątek potrafi zawrzeć tę wiedzę — jeśli się nie da jakoś inaczej.

Pozostało 580 znaków

2018-11-22 15:19
12
nobody01 napisał(a):

Ludzie piszą, że sterowanie przepływem za pomocą wyjątków to zło. Wydaje się to oczywiste, ale niektórzy inaczej to rozumieją: https://devstyle.pl/2016/11/2[...]tat-wykonania-komendy-w-cqrs/ Pytanie: w jakich sytuacjach warto rzucić wyjątek zamiast zwrócić obiekt rezultatu? Są w ogóle takie sytuacje?

Nie czytaj niczego, co Aniserowicz pisze o wyjątkach. To, że on sobie steruje przepływ wyjątkami, nie znaczy, że każdy musi. Z jego podejściem możesz mieć problem ze znalezieniem zatrudnienia. On nie pracuje jako programista, więc nie musi się martwić o jakośc kodu.

Althorion napisał(a):

Jeśli język nie ma Maybe, Optional itd. i po prostu nie da się inaczej zasygnalizować, że albo będzie wynik, albo będzie śmieć, to rzucenie wyjątku pozwala nam to jakoś obejść.

Jeśli nie ma Maybe, Optoinal czy Either, to sobie można je dopisać. W językach, w których nie da się dopisać, nie ma też raczej wyjątków, więc problem nie istnieje.

Wyjątki są do sytuacji wyjątkowych, czyli:

  1. Błąd interakcji ze światem zewnętrznym (bazą danych, webserwisem, plikiem).
  2. Do pewnego miejsca w kodzie dotarły bądź nie dotarły dane, które nie powinny się tam znaleźć, więc nie wiadomo co z nimi zrobić. Bo np. jakimś cudem nieprawidłowa wartość przedostała się przez walidację, albo zmienił się jakiś kontrakt, a my nie zaimplementowaliśmy obsługi tej zmiany.

"HUMAN BEINGS MAKE LIFE SO INTERESTING. DO YOU KNOW, THAT IN A UNIVERSE SO FULL OF WONDERS, THEY HAVE MANAGED TO INVENT BOREDOM."
Uważaj, bo Jak Maciej się dowie co piszesz to cię skrytykuje w swojej flaperowej prezentacji albo nakręci o tym odcinek na bloga. - ._. 2018-11-23 03:14
To następnym razem go obleję piwem na konfie. - somekind 2018-11-23 03:16
Sami alkoholicy, nie znacie lepszych używek niż alko.? - ._. 2018-11-23 03:29
Nie sami - tylko ja. - somekind 2018-11-23 10:42

Pozostało 580 znaków

2018-11-22 15:33
2

Zauważ że ten artykuł jest w kontekście zaproponowanej przez niego prostej synchronicznej implementacji CQRS, więc nie wysnuwałbym daleko idących wniosków, i nie interpolowałbym bym tego na kod pisany w innym kontekście.

Natomiast ja stosuje takie rozgraniczenie:
Wyjątek rzucasz wtedy kiedy w przypadku błędu nie wiesz co dalej zrobić na danym poziomie abstrakcji lub poziom wyżej, jak wiesz co zrobić, zwracasz rezultat.


#Dżunior React Devloper wanna be#
Nie interpoluję, on wiele razy pokazywał, że steruje przepływem przez wyjątki. - somekind 2018-11-22 15:48

Pozostało 580 znaków

2018-11-22 16:48
1

Starczy odróżnić wyjątki (sytacje wyjątkowe: brak internetu, baza się sypnie, jakaś usługa padnie) od błędów (zazwyczaj zły input od użytkownika). W pierwszym wypadku można rzucić wyjątkiem (acz należy też pamiętać, że jest to kosztowne. Rzadko kiedy to ma znaczenia, ale może). Natomiast w drugiej sytuacji najeży to uwzględnić w jakimś procesie biznesowym (czy czymś podobnym) i odpowiednio zamodelować.


Spring? Ja tam wole mieć kontrole nad kodem ᕙ(ꔢ)ᕗ
Haste - mała biblioteka do testów z czasem.

Pozostało 580 znaków

2018-11-22 19:00
10

Generalnie jest pewna zasada:

  • jeśli problem leży po stronie wywołującego (bo np. podał złe argumenty do funkcji) to raczej zwracamy specjalny rezultat (Either, Option, whatever), logiczny odpowiednik błędów 4xx w HTTP
  • jeśli problem leży po stronie wywoływanego - coś nam się rypło i wina nie leży po stronie klienta to siejemy panikę i rzucamy Exception (RuntimeException), logiczny odpowiednik błędów 5xx w HTTP

Na te pierwsze błędy możemy też patrzeć jak na Recoverable, bo klient ma szansę się poprawić i wywołać z innymi parametrami.
Te drugie to błedy typu Panic. Co byś nie zrobił to nic nie poradzisz, możesz wołać funkcję raz jeszcze i pewnie dostaniesz taki sam błąd. Dalej nie przejdziesz.

Przykład. Jest taka funkcja:

policzMiŚredniąOcenDlaUcznia(nrUcznia) : Either<Wtf, Double> 

Dajemy ten Either, bo może ucznia nie ma w bazie i zwrócimy - Either.left(Wtf.NieMaTakiegoNumeru). Lub uczeń nie ma żadnych ocen - Either.left(Wtf.NoStaaaary).

Ta funkcja pod spodem może robić Query na bazie danych , wołając coś takiego;:

bazaExecuteQuery(sql) : Either<SQLWtf, Result>

I ta funkcja znowu zwraca nam rezultat query (pewnie jakąś listę/liczbę) - Either.right(3.2); Lub zwraca Either.left(new SQLWTF.TwojSQLToZuooo(" DROP DATABASE? Ale za co?" )), bo zrobiliśmy błędne składniowo zapytanie SQL.
W drugim przypadku wina leży po naszej stronie (funkcji liczącej średnią) i obsługując tego Either rzucamy klientowi Exception("Kurde, sorry");, bo biedak i tak nie może nic zrobić. To typowy przykład kiedy Recoverable błąd niższej warstwy przechodzi w Panic/Wyjątek w warstwie wyższej.

Oczywiście jest dużo przypadków, kiedy to rozróżnienie jest dość szare/płynne.

Np. wiadomo, że jeśli zerwało się połączenie z bazą danych to policzMiŚredniąOcenDlaUcznia powinno rzucić Exceptionem (Runtime/Panic), ale co w tej sytuacji zrobić powinien wewnętrzny bazaExecuteQuery(sql)?
Z pewnego punktu widzenia to błąd wywoływanego, klient tego chciał tylko wykonać SQL i podawanie innego SQL nic nie pomoże.
Z innego punktu widzenia to klient, powinien się upewnić, że połączenie jest aktywne, a w razie zerwania je odnowić. (czyli jednak da się naprawić).

Te zasady są ukradzione po zasadach CheckException vs RuntimeException, z tym że po prostu uznajemy, że CheckedException to nieudany eksperyment i zastępujemy to Eitherami (głównie, bo mamy też inne możliwości).
Podobnie się to robi np.: w Haskellu (Maybe, Either vs Panic).


Bardzo lubie Singletony, dlatego robię po kilka instancji każdego.
edytowany 6x, ostatnio: jarekr000000, 2018-11-22 21:14
Ja często robię tak, że mam dwie metody np wyciągające dane - np findUser zwracające Optiona (lub inną monadę o ile takiej potrzebuję), oraz findUserOrThrow. Unikam w ten sposób wrapowania Optiona w wyjątek w wielu miejscach i robię to w jeden spójny sposób. Nie mówię, że tak jest lepiej lub gorzej - aktualnie wygodnie mi się tego używa, ale może kiedyś się na tym rozwalę i zmienię zdanie - pustypawel 2018-11-27 13:37

Pozostało 580 znaków

2018-11-23 04:10
._.
0

Jeśli nie ma Maybe, Optoinal czy Either, to sobie można je dopisać. W językach, w których nie da się dopisać, nie ma też raczej wyjątków, więc problem nie istnieje.

W .Net polecam tą implementacje, jest świetna, bardziej mi się podoba niż ta z Javy
https://github.com/nlkl/Optional

Tyle że tam optional też jest Eitherem

Option<T, TException>

I wtedy możesz po prostu użyć:

some.WithException(ErrorCode.GeneralError)
some..WithoutException()

Pozwala też wybrać wartość Unsafe bez mapa i etc i zawsze zostaniesz powiadomiony o tym, że robisz to na własną odpowiedzialność odpowiednim namespace
Optional.Unsafe

edytowany 1x, ostatnio: ._., 2018-11-23 05:44

Pozostało 580 znaków

2018-11-23 08:02
1

Pytanie: w jakich sytuacjach warto rzucić wyjątek zamiast zwrócić obiekt rezultatu?

Kiedy uformołeś odpowiedź do klienta, ale po pięciu próbach okazuje się, że nie ma już połączenia. Gdy nagłówek wiadomości mówi, że mamy 40 bajtów danych a bajtów jest 45 i to jakimś cudem przeszło wcześniej walidacje. Gdy podczas obliczeń próbujesz dzielić przez zero itd. itp. Tak z własnej praktyki widzę, że bardzo rzadko natrafiamy na sytuację kiedy rzucenie wyjątku faktycznie wydaje się najlepszym rozwiązaniem.

edytowany 1x, ostatnio: several, 2018-11-23 08:09

Pozostało 580 znaków

2018-11-23 08:31
eL
0
jarekr000000 napisał(a):

Generalnie jest pewna zasada:

  • jeśli problem leży po stronie wywołującego (bo np. podał złe argumenty do funkcji) to raczej zwracamy specjalny rezultat (Either, Option, whatever), logiczny odpowiednik błędów 4xx w HTTP

@jarekr000000 A co w systuacji kiedy np. Service A woła Service B np. getCośTamCośTamById(id). Leci request do B, szuka szuka, nie znalazł nic co by mu pasowało więc:

  1. Rzuca odpowiedź ze statusem 404 Not found
  2. Rzuca odpowiedź ze statusem 200 a w odpowiedzi podkleja to co znalazł czyli null a klient opakowuje sobie to na jakiś Option i dalej przerabia
  3. Rzuca odpowiedź ze statusem 200 i w odpowiedzi podkleja gotowy Option który może być pusty jeśli nic nie znalazł

Temat raczej ogólny więc niechciałbym wchodzić w szczegóły implementacji ale pytam w kontekście wyjątków bo np. Spring przy zwracaniu ResponseEntity ze statusem 404 zamienia to na wyjątek przez co później takie triki

 if(response.getStatusCode().is2xxSuccessful()){...}

nie mają żadnego sensu bo i tak tu nie wpadnie i trzeba łapać wyjątki. Jest to oczywiście do zrobienia ale zastanawia mnie wówczas jaki jest sens kodów http w takim przypadku i czy właśnie większego sensu nie ma Twój pomysł z Option.

Druga sprawa to od razu zastanawia mnie że skoro Spring zamiast response 404 rzucać exception to musieli mieć w tym jakiś pomysł a pomysł ten jest całkowicie odwrotny do Twojego.

edytowany 3x, ostatnio: eL, 2018-11-23 08:37
Nie musisz używać springa. - jarekr000000 2018-11-23 08:49
Oczywiście wiem że nie muszę ale spring z jakiegoś powodu tak robi więc podsumowując moje 2 pytania to: 1. Jak z tymi odpowiedziami jeśli service b nic nie znalazł? Rozumiem że mój przykład to jakaś specyficzna Springowa implementacja ale jeśli tak ogólnie na to spojrzeć to co powinno być zwrócone jeśli service b nie znalazł nic po kryteriach? 2 Czy wiesz jaki sens Pivotal widział w tym żeby to opakować w Exception? Próbowałem korzystać z tego ResponseEntity ale jest to kompletnie nieintuicyjne przy rzucaniu wyjątków jak status code jest >400 - eL 2018-11-23 08:53

Pozostało 580 znaków

2018-11-23 10:30
0

Przechodzenie przez HTTP to zupełnie inna bajka i nie wiem co w tej sytuacji jesl lepszą opcją.
Jak to serwisy wewnątrzfirmowe / niepubliczne - to zwracaniew zawsze 200 i jakiegoś obiektu {status: 'not found', result: null} jest całkiem ok, (mimo, że wygląda jak rak to się sprawdza, a poza tym chrzanić RESTy).
Jak trzeb dochować wierności REST i statusom / api jest publiczne to po prostu bym odpakowywał te wyjątki jakimiś util metodami. Tylko nadal nie rozumiem skąd się biorą, naprawdę client web springa tak robi ? jest gdzieś doc do tego? nie ma tak jakiegoś onStatus, co można zdefiniować itp.?


Bardzo lubie Singletony, dlatego robię po kilka instancji każdego.
edytowany 1x, ostatnio: jarekr000000, 2018-11-23 10:43
Co do doc'a to spróbuję coś zaraz googlnąć. Btw chrzanić resty? Jakaś alternatywa? - eL 2018-11-23 10:46
W sensie, nie zawsze warto dię przejmować filozofią REST i REST maturity level. Przeważnie podkreślam, że robimy po prostu serwisy jsonowe... a nie RESTy. Jak się robi publiczne API to elegancko jest dochować standardów. Ale wewnątrz firmy, ze wzgledu na infrastrukturę / framewoirki czasami nie warto. W jednej firemie musieliśmy zawsze dawać 20x, i opakowaywać w status object. początkowo mnie to śmieszyło, ale okazało się całkiem sprawne i miało pewne uzasadnienie w architekturze. - jarekr000000 2018-11-23 10:53

Pozostało 580 znaków

2018-11-23 10:42
1

Druga sprawa to od razu zastanawia mnie że skoro Spring zamiast response 404 rzucać exception to musieli mieć w tym jakiś pomysł a pomysł ten jest całkowicie odwrotny do Twojego.

No no, "ktoś mądrzejszy to wszystko wymyślił." Springa też piszą ludzie, którzy też mogą popełniać błędy. 404 powinien być normalnym elementem przepływu, nie ma tu żadnej sytuacji wyjątkowej, wszystko jest opisane przez protokół.

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0