Wyjątki vs obiekty reprezentujące błąd/rezultat

0

Pomijam Javę i inne języki javo-podbne, gdzie to jest niewykonalne. Bardziej chodzi mi o PHP, Ruby itp. (dlatego też ten temat jest tutaj, a nie w dziale PHP).

Dzisiaj przy CR'ce ktoś mi zaproponował, żeby zamiast walić wyjątki typu: TooManyAttemptsException, InvalidVerificationCodeException zwracać obiekt zawierający kod błędu (obiekt, zamiast inta, po to, żeby móc w razie czego przekazać kontekst). W PHP jest to legitne, bo wystarczy nie zadeklarować typu zwracanego i róbta co chceta.

Coś mi się nie bardzo to podoba. Juz bym wolał zwrócić boola, jeżeli metoda zrobiła, to co miała zrobić. W internetach piszą, że wyjątki powinny być zarezerwowane dla wyjątkowych sytuacji. Ja się spodziewam, ze użytkownik może podać zły kod, albo zrobi to za dużo razy, więc nie potrafiłem znaleźć na to kontrargumentu poza tym, że hackowanie typu zwracanego przez nie deklarowanie go i używanie jakiś własnych rozwiązań zamiast prawilnych wyjątków jakoś mi nie leży.

0

jezeli nada sytuacja nie jest obsluzona to moim zdaniem jest ona wyjatkowa i powinnien poleciec wyjatek

Zwracanie kodu czy obiektu ktory zawiera kod wymaga dodatkowe mapowanie. Gdzies bedziesz musial miec dokument czy cokolwiek innego co dokldanie to znaczy
z wyjatkiem sytuacja sie upraszcza

6
  1. Oczywiście że się da to zrobić w Javie, za pomocą Either<ResultType, ErrorType>
  2. A jak metoda normalni zwaca boola to co? Jakieś bazodanowe exists(), co wtedy zrobisz? False oznacza ze się wywaliło czy że nie ma takiego elementu w bazie? A jak różnych błędów może być 10 typów to co? Jak to tym boolem zrobisz? Rodem z C będziesz ustawiać jakąś magiczną gobalną zmienną ERRNO? :D
  3. Jeśli obsługujesz problem od razu to NIE rzucasz wyjątku. Wyjątki mogą sie przydać jak chcesz "wyskoczyć" przez pół systemu, bo nie da się czegoś obsłużyć wcześniej. Wyobraź sobie że piszesz grę sieciową. Chcesz wykonać ruch gracza, ale połączenie sieciowe zostało zerwane. User wciska na klawiaturze w, kod próbuje wysłać komendę do serwera, ale dostaje błąd z socketu, że już zamknięty. Co teraz? Jesteśmy gdzieś głęboko w logicę poruszania graczem i nijak stąd nie możemy nic zrobić. Trzeba wyskoczyć gdzieś znacznie wyżej i tą sytuację obsłużyć. Z drugiej strony jak pytasz usera o podanie liczby a on podaje lierki to nie ma sensu robić wyjątku, tylko dać po prosu ifa albo pętlę.
1
Shalom napisał(a):
  1. Oczywiście że się da to zrobić w Javie, za pomocą Either<ResultType, ErrorType>

Niuansik. Ze względow politycznych/ konwencję piszemy:

Either<ErrorType, ResultType>

Prawy rezultat jest prawy - czyli dobry. Np. podstawowa operacja map operuje na prawym argumencie.

0

Wezmę udział w wątku z opowieścią.
Raz z powodzeniem i zadowoleniem robiłem coś podobnego, poprzez protokół Apache Thrift.
Kto nie zna Thrifta, rodzaj nowocześniejszego RPC.

Tam jest możliwość w definicji IDL zadeklarowania


/**
 * Structs can also be exceptions, if they are nasty.
 */
exception ServiceException {
  1: i32 what,
  2: string why
}

przy czym dokumentacja mówi

Exceptions are functionally equivalent to structs, except that they inherit from the native exception base class as appropriate in each target programming language, in order to seamlessly integrate with the native exception handling in any given language.

https://thrift.apache.org/docs/types

Serwis musi (powienien) łapac wyjątki po to, aby je przepakować w to dedykowane exception i je rzucić. Biblioteka tym tak operuje, że wyjątek jest przetransmitowany na zdalnego klienta "zamiast" oczekiwanego rezultatu (de facto siecią gania podobne do powyższego tupple /pair).
W tym wdrożeniu 90% wyjątków ma charakter biznesowy np "ten tym dokumentu na tą kategorię nie może być wykonany" i żaden enduser nie umarł, jak to zobaczył (na tablecie), nawet z krótkim kawałkiem stacktrace. Bardzo fajne doświadczenie, choć efekt w zasadzie nie moimi rękami.

Ale to znaczna mniejszość, by nie rzecz wyjątek ;) W ogólnym przypadku "zwróć coś albo error" (ew "zwróc coś albo ustaw globalny error") przypomina mi najgorsze doświadczenia C. Brrrr.
Obiema ręcyma popieram "szkolne" zasady łapania wyjątków, na czele z "łap tam, gdzie będziesz umiał z tym coś zrobić".

0
Desu napisał(a):

Pomijam Javę i inne języki javo-podbne, gdzie to jest niewykonalne.

To jest banalne do zaimplementowania.

hackowanie typu zwracanego przez nie deklarowanie go i używanie jakiś własnych rozwiązań zamiast prawilnych wyjątków jakoś mi nie leży.

To nie jest hackowanie, to jest poprawny design i poprawne nieużycie wyjątków dla niewyjątkowych sytuacji.

jarekr000000 napisał(a):

Niuansik. Ze względow politycznych/ konwencję piszemy:

Either<ErrorType, ResultType>

@jarekr000000: ja tam bym to zrobił jeszcze prościej: EitherErrorOr<Type>, bo jakoś nigdy nie miałem potrzeby tworzenia hierarchii typów błędów.

1
somekind napisał(a):

@jarekr000000: ja tam bym to zrobił jeszcze prościej: EitherErrorOr<Type>, bo jakoś nigdy nie miałem potrzeby tworzenia hierarchii typów błędów.

Tu są takie opcje (w vavr - i prawie wszędzie)
Option<Type> czyli programistyczne nie ma to nie ma, po .... drążyć
Try<Type> - jakise Exception był, ale Cię nie obchodzi jaki, byle stack trace był (to ten odpowiedznik twojego EitherErrorOr)
Either<E, T> jest najbardziej dokładne, bo właśnie umożliwia opisanie błędu w jakiś rozsądny sposób (to niejako zastępuje javowe Checked exceptions - czyli takie exceptiony, gdzie potencjalnie klient chciałby wiedzieć co zaszło i może jakoś wybrnąć ( ang. recover :-) ) ). Ale za to nie ma Stack Trace (i dobrze!).

0

Ja tam się nie trzymam jakichś enterprise standardów, robię to, co wygodne dla mnie. I nie mam żadnego stack trace, bo to jest Error, a nie Exception. A, że mam tylko jeden typ Error, to ten pierwszy parametr generyczny w Either nic by mi nie dał.
Gdybym miał taki Eitner<T1, T2> z jakiejś biblioteki, to i tak bym go opakował w EitnerErrorOr<T2> : Eitner<Error, T2>. Jakoś nie widzę sensu powtarzania nazwy typu w tylu miejscach.

1

Leci wyjątek przez wiele modułów - nie ma problemu.
Leci kod błędu przez wiele modułów - dużo roboty poprawiania bo każdy moduł musi to obsłużyć nie ważne czy potrzebuje czy nie.
IMO kody błędu mają sens w przypadku programowania rzeczy asynchronicznych, wtedy wyjątki po prostu nie mają sensu.

1
MarekR22 napisał(a):

Leci wyjątek przez wiele modułów - nie ma problemu.

Jest problem, bo po pierwsze trzeba się wtedy zastanawiać, czy to błąd biznesowy czy wyjątek.

Leci kod błędu przez wiele modułów - dużo roboty poprawiania bo każdy moduł musi to obsłużyć nie ważne czy potrzebuje czy nie.

Nie musi niczego obsługiwać, może po prostu przekazać dalej. To tylko kwestia typu zwracanego z metody.

IMO kody błędu mają sens w przypadku programowania rzeczy asynchronicznych, wtedy wyjątki po prostu nie mają sensu.

Tego to ja już w ogóle nie rozumiem, co ma jedno do drugiego?

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