Są tu jeszcze jacyś miłośnicy checked exceptions? Używacie?

0

Takie mnie pytanie naszło pod wpływem świata poznanego

0

Pomysł fajny, ale zbyt często są nadużywane. Dla przykładu nie rozumiem trochę czemu IOException jest checked.

Użyłbym checked wyjątku, wtedy kiedy można jednoznacznie stwierdzić że wyjątek jest na 100% częścią interfejsu metody. Wtedy bym go dodał, bo jeśli jest częścią interfejsu, to dobrze żeby kompilator o tym wiedział.

Tylko takie przypadki to jest może 1 na 1000. Pozostałe 999/1000 to case'y gdzie wyjątek to część implementacji (nie interfejsu) i nie powinien być checked.

2

Użyłbym checked wyjątku, wtedy kiedy można jednoznacznie stwierdzić że wyjątek jest na 100% częścią interfejsu metody

Wtedy nie jest wyjatkiem tylko wartoscia Either/Result/WstawCokolwiek

0
stivens napisał(a):

Użyłbym checked wyjątku, wtedy kiedy można jednoznacznie stwierdzić że wyjątek jest na 100% częścią interfejsu metody

Wtedy nie jest wyjatkiem tylko wartoscia Either/Result/WstawCokolwiek

Nie wartościuję które podejście jest lepsze - czy zwrócenie błędu/either/result czy rzucenie wyjątku. Każdy może stosować co chce.

Ale jeśli mamy metodę, z której leci wyjątek, którego zawsze, ale to zawsze trzeba złapać; bo tak po prostu jest zaprojektowana ta metoda; to wtedy moim zdaniem checked wyjątki są okej.

Jeśli ktoś chce zrefaktorować metodę żeby nie rzucała wyjątków, tylko zwracała Either/Result - be my guest. Ale jeśli nie, i ktoś się zdecyduje zostawić wyjątek, który zawsze trzeba łapać, to moim zdaniem checked wyjątki są okej. Jednak jak wspominałem, to jest 1 na 1000.

2
stivens napisał(a):

Użyłbym checked wyjątku, wtedy kiedy można jednoznacznie stwierdzić że wyjątek jest na 100% częścią interfejsu metody

Wtedy nie jest wyjatkiem tylko wartoscia Either/Result/WstawCokolwiek

Dokładnie. A szczególnie bez sensu jest to, że CheckedException ma tak czy siak stacktrace - potencjalnie kosztowny. Po co, skoro wiadomo skąd leci?

0
jarekr000000 napisał(a):
stivens napisał(a):

Użyłbym checked wyjątku, wtedy kiedy można jednoznacznie stwierdzić że wyjątek jest na 100% częścią interfejsu metody

Wtedy nie jest wyjatkiem tylko wartoscia Either/Result/WstawCokolwiek

Dokładnie. A szczególnie bez sensu jest to, że CheckedException ma tak czy siak stacktrace - potencjalnie kosztowny. Po co, skoro wiadomo skąd leci?

Nie wiadomo. Przecież to może być z dowolnego miejsca z dołu stacka, i może też być potencjalnie polimorficzny.

A odnośnie tego co @stivens napisał, to chyba istnieje pewne nieporozumienie.

Nie mówię że wyjątki są lepsze niż Either/Result - możecie stosować co chcecie. Ja tylko mówię że jeśli ktoś chce wyjątek (i to jest given), to kiedy ten wyjątek leci zawsze, to checked exceptiony są spoko (zakładając ze trzeba go zawsze złapac). Ale jeśli ktoś nie chce wyjątków, tylko woli either/result to to też jest spoko. Bo jednak w 999 na 1000 przypadków, nie zawsze trzeba go łapać.

1
Riddle napisał(a):

Nie wiadomo. Przecież to może być z dowolnego miejsca z dołu stacka, i może też być potencjalnie polimorficzny.

Jeśli trzeba zaglądać do kodu takiego API (numery linii w stack trace), żeby zobaczyć co się stało to i tak jest to słabe API. (a polimorficzność to zupełnie niezależna rzecz).

0
jarekr000000 napisał(a):
Riddle napisał(a):

Nie wiadomo. Przecież to może być z dowolnego miejsca z dołu stacka, i może też być potencjalnie polimorficzny.

Jeśli trzeba zaglądać do kodu takiego API (numery linii w stack trace), żeby zobaczyć co się stało to i tak jest to słabe API.

No okej, to faktycznie checked wyjątki mogłyby nie mieć stacktrace'a. Pytanie tylko na ile to byłaby mikrooptymalizacja.

1
Riddle napisał(a):

No okej, to faktycznie checked wyjątki mogłyby nie mieć stacktrace'a. Pytanie tylko na ile to byłaby mikrooptymalizacja.

Tak naprawdę to JITy od dawna mocno optymalizują te stack trace'y (np. często nie są faktycznie konstruowane, jeśli nikt ich potem nie czyta).
Ale jest to dość duża komplikacja, która mocno utrudnia (przez komplikację kodu) robienie wielu dalszych optymalizacji.
Podobno nawet Tail Call Optimization jest poniekąd blokowane przez stacke trace i wyjątki (ale nigdy nie do końca zrozumiałem dlaczego, bo imo da się zrobić - po prostu komplikacja).

W praktyce to nawet nie tyle CE co w ogóle powszechność wyjątków w kodzie javy, a API platformy, jest problemem.

Dobry przykład to np. Integer.parseInt, który deklaruje NumberFormatException (co ciekawe nie jest to CE tylko Runtime).
I potencjalnie każda aplikacja, która parsuje jakieś inty może tych exceptionów rzucać dziesiątki tysiący na sekundę.
(kilka razy widziałem problem wydajnościowy na produkcji spowodowany właśnie takimi radośnie rzucanymi wyjątkami).

0
jarekr000000 napisał(a):

W praktyce to nawet nie tyle CE co w ogóle powszechność wyjątków w kodzie javy, a API platformy, jest problemem.

Z tym się zgadzam. Ale @KamilAdam nie pytał o powszechność wyjątków w stdlib, tylko o checked wyjątki, dlatego chciałem się wypowiedziec na temat.

jarekr000000 napisał(a):

Dobry przykład to np. Integer.parseInt, który deklaruje NumberFormatException (co ciekawe nie jest to CE tylko Runtime).
I potencjalnie każda aplikacja, która parsuje jakieś inty może tych exceptionów rzucać dziesiątki tysiący na sekundę.
(kilka razy widziałem problem wydajnościowy na produkcji spowodowany właśnie takimi radośnie rzucanymi wyjątkami).

No ale w sumie do czego zmierzasz? Wiadomo że jak apka rzuca dziesiątki tysięcy wyjątków na sekundę, to to jest problem, ale to nie wina wyjątków samych w sobie, tylko niepoprawnego użycia ich. Każde narzędzie można nadużyć.

2
Riddle napisał(a):

No ale w sumie do czego zmierzasz? Wiadomo że jak apka rzuca dziesiątki tysięcy wyjątków na sekundę, to to jest problem, ale to nie wina wyjątków samych w sobie, tylko niepoprawnego użycia ich. Każde narzędzie można nadużyć.

Tu zmierzam do tego, że stdlib javy niejako taki sposób (Exception Oriented Programming) promuje. Jak nie chcesz wyjątku przy parseInt to trzeba się bawić w jakieś regexpy przed wrzuceniem do parse.
Wiekszość ludzi walnie parseInt. Przed wyjebką na produkcji często nawet nie da się przewidzieć jak często exception będzie leciał, a wystarczy jakaś pomyłka u klienta w kodzie i mamy przypadkowy DOS.

Co gorsza nauczeni patentów z libki standardowej ludzie powtarzają je w swoich aplikacjach.

A wystarczyłoby, żeby parseInt dawało jakiś Either/Result. Zdaję sobie sprawy, że to było pisane w czasach kiedy Eithery były prawie nieznane, a wyjątki to była cool nowość,
po prostu oceniam z perspektywy obecnej.

0

No ale to mówisz o problemie wydajnościowym z wszystkimi wyjątkami. I z tym się nie będę kłócił. (mimo że osobiście uważam że jednym z wyjść jest Either, fakt, ale innym wyjściem mogłoby też być isInt(String) który zwraca true/false).

Ale tak czy tak; to odbiega od tematu, bo @KamilAdam nie pytał czy wyjątki są dobrym pomysłem; tylko czy używacie checked wyjątków. Moim zdaniem to inny temat, i trochę odbiegliśmy.

1

To jest dokładnie w temacie.

Po prostu uważam, że każde obsługiwalne wyjątki (czyli CE) to błąd.

Wyjątki katastrofalne, których zasadniczo się nie obsługuje (obsługa jest minimalna - np. zwrócenie kodu/stack trace) są ok (i wtedy stack trace jest też ok).

Kolejny argument to też zaśmiecanie języka. To ten sam problem co async czy suspend (z kotlina).
Mamy specjalnie dodany znacznik do typów, który jest związany z jednym rodzajem efektu (a rodzajów efektów jest dużo więcej).
Zresztą throws rozsiewa się w kodzie, w sposób bardzo podobny do async.

Czyli dla mnie to błąd projektowy javy (nieudany eksperyment). (Chociaż z punktu widzenia informatyki, dobrze, że ktoś ten eksperyment zrobił).
Tworzenie kolejnych API z CE tylko przedłuża agonię tego tworu.

0
jarekr000000 napisał(a):

To jest dokładnie w temacie.

Po prostu uważam, że każde obsługiwalne wyjątki (czyli CE) to błąd.

Wyjątki katastrofalne, których zasadniczo się nie obsługuje (obsługa jest minimalna - np. zwrócenie kodu/stack trace) są ok (i wtedy stack trace jest też ok).

Czyli jak w kodzię robię catch() na RuntimeException, to jest to błąd według Ciebie?

jarekr000000 napisał(a):

Kolejny argument to też zaśmiecanie języka. To ten sam problem co async czy suspend ( z kotlina).
Mamy specjalnie dodany znacznik do typów, który jest związany z jednym rodzajem efektu (a rodzajów efektów jest dużo więcej).
Zresztą throws rozsiewa się w kodzie, w sposób bardzo podobny do async.

W sumie fakt, aczkolwiek, zaśmiecanie zaśmiecaniu nie równe. Patrząc tylko na "estatykę" języka i kodu, to nie jestem do końca pewny czy Either/Result jest spoko, bo z prostego typu robimy generyk. Wyjątki są jednym ze sposobów ogarnięcia tego.

Poza tym nadal; jedyny argument przeciwko wyjątkom zwykłym jak rozumiem, to jest performance, tak?

1

Wartosci sie tez lepiej komponuje. W CE obsluga bledow jest wymuszona, a jednoczesnie duzo bardziej "pokraczna" niz dla efektow, bo jak masz wartosc to mozesz zrobic np.

canFailA.orElse(canFailB).fold(defaultValue, _)

EDIT: Jeszcze kilka przykladow: (z dyskusji pod postem)

canFail
  .logOnError
  .retry(retryPolicy)
Resource(
  acquire = openFile
)(
  release = file => closeFile(file)
).use(file => readLine(file))
.logOnError
0
stivens napisał(a):

Wartosci sie tez lepiej komponuje. W CE obsluga bledow jest wymuszona, a jednoczesnie duzo bardziej "pokraczna" niz dla efektow, bo jak masz wartosc to mozesz zrobic np. canFail.fold(defaultValue, _) albo canFailA.orElse(canFailB)

TBH nie sądzę że to jest zaleta.

Dlatego że jeśli obsługa błędów jest prostsza z Result, z tymi fold i orElse(), to to zachęca do częstszego używania ich. A moim zdaniem nie powinieneś być bardziej skłonny używać ich; tylko raczej zaprojektować software który po prostu ma tego mniej. I jak masz mało takich miejsc, (np jedno w kodzie), to w sumie co za różnica czy zrobisz .orElse() czy catch.

1
Riddle napisał(a):

Czyli jak w kodzię robię catch() na RuntimeException, to jest to błąd według Ciebie?

Nie. Robić gdzieś catch(), żeby zalogować problem i zwrócić jakiś błąd klientowi. Natomiast zasadniczo nie obchodzi cię co to byl za błąd, i nie robisz obsługi (choć jakies akcje typu retry są czasem dopuszczalne). Ważne, że liczba tych catch w kodzie jest bardzo mała.

Genealnie RuntimeException to znaczy - pojawił się błąd, którego zdecydowałem się nie obsługiwać, bo to a) pracochłonne b) bezcelowe.
Mogę np. zdecydować, że jak nie mogę połączyć się z bazą danych to rzucam RuntimeException, bo nie widzę sensu kontynuować obsługi żądania. Z drugiej strony świetnie by było, gdyby biblioteka standardowa javy dawała mi możliwość łączenia do bazy bez rzucania wyjątkiem. (bo np. robię generyczny database browser, gdzie URL do bazy jest podawany przez użytkownika - i to, że akurat nic nie ma pod danym URL to normalna sytuacja).

Podobnie z parseInt , mógłbym gdzies stwierdzić, że dany int pochodzi z jakiegoś wewnętrznego konfiga i jeśli nie jest poprawny, to jest to tak absurdalna sytuacja, że używam jakiegoś getOrThrow rzucam exception, a gdzies w najwyższej warstwie jest catch, który to łapie i kończy np. prace systemu (uprzednio rzucając jakieś alerty/logi). Ważne, że podejmuje świadomie tą decyzję i wiem, że np. nie ma szans, żeby ten exception był rzucany tysiące razy na sekunde.

jarekr000000 napisał(a):

Poza tym nadal; jedyny argument przeciwko wyjątkom zwykłym jak rozumiem, to jest performance, tak?

Jeśli chodzi o faktyczne RuntimeException - sytuacje typu panic to zasadniczo nie ma żadnego argumentu przeciwko.
Powinny zdarzać się na tyle rzadko, że wydajność nie ma znaczenia.

0
jarekr000000 napisał(a):

Poza tym nadal; jedyny argument przeciwko wyjątkom zwykłym jak rozumiem, to jest performance, tak?

Jeśli chodzi o faktyczne RuntimeException - sytuacje typu panic to zasadniczo nie ma żadnego argumentu przeciwko.
Powinny zdarzać się na tyle rzadko, że wydajność nie ma znaczenia.

Trąci mi premature-optiamlization ten argument.

Dużo jest takich miejsc które są potencjalnie performance'owymi bottleneckami, ale z reguły nie warto ich rozwiązywać dopóki nie wystąpią.

1

Argumenty przeciwko wyjątkom

  1. Ukryty przepływ sterowania - może j'bnąć z zaskoczenia
  2. Utrudnione kumulowanie błędów - wywołuje f1, wywołuje f2 jeśli oba się nie powiodą chce zwrócić wspólny błąd. Jeśli jedna to błąd z tej co się nie udała. Jeśli obie się udały to wspólny rezultat
  3. Performance
  4. Zwyczajna brzydkość obsługi

Ad.4. Chce sparsować stringa a jak się nie uda to zwrócić 0

Z wyjątkami

Long result = 0
try {
  result = parseLong(input)
} catch (RuntimeException e) {
  //logować? nie logować? chyba nie logować. Pewnie się sonar czepi iż nie loguje
}

Bez wyjątków

var result = parseLongOpt(input).getOrElse(0);

Na szczęście mamy Try więc pewnie więc można zrobić coś takiego

var result = Try(() -> parseLong(input).getOrElse(0);

Czyli ukryć zakopać, nie używać

1
Riddle napisał(a):

Trąci mi premature-optiamlization ten argument.

Trochę racja, ale to skomplikowane.

Pojedyncze rzucanie exceptionem można wyłapać i naprawić.

Ale tu mówimy o regularnym używaniu w API, rzucaniu potencjalnie tonami exceptionów w normalnym działaniu kodu.

Różne JITy sobie z tym radzą lepiej lub gorzej, ale generalnie stanowi to narzut, trudny nawet do oszacowania. Miałem przykłady, że wywalenie wyjątków (i to nie wszędzie, ale tych najbardziej widocznych w profilingu) zwiększało wydajność o prawie połowę, a czasem nawet wielokrotnie (to był jeden bardzo extremalny przypadek Exception oriented programming, aplikacja działała "jak rzygi" nawet przy jednym użytkowniku). Z tym, że co ważne to raczej dawne czasy jeszcze do javy 8. Obecnie się z tym nie spotykam: a) bo już nie mam kodu z wyjątkami(scala, kotlin), b) JITy sobie lepiej radzą.

Z drugiej, kiedyś będziemy mieli w końcu JIT zrobiony profesjonalnie (czyli przez jakieś AI) - więc faktycznie problem wydajności zniknie całkowicie.

0
KamilAdam napisał(a):

Argumenty przeciwko wyjątkom

  1. Ukryty przepływ sterowania - może j'bnąć z zaskoczenia
  2. Utrudnione kumulowanie błędów - wywołuje f1, wywołuje f2 jeśli oba się nie powiodą chce zwrócić wspólny błąd. Jeśli jedna to błąd z tej co się nie udała. Jeśli obie się udały to wspólny rezultat
  3. Performance
  4. Zwyczajna brzydkość obsługi

Nie przekonują mnie te argumenty konkretnie.

Ad. 1. Ukryty, owszem, ale przynajmniej nie masz Result/Either z generykami rozwalonego po kodzie wszędzie. Najgorszy case to jak ten Either jest z jakiejś biblioteki, to wtedy ciężko od niej odejść/zmienić, twarda zależność.

Ad. 2. Imo jeśli chcesz kumulować wyniki to to jest bardzo silny argument za tym że robisz coś co nie powinno być na wyjątkach (np. walidacja). Ludzie mają takie (dziwne) przekonanie, że jak masz walidację (np maila, username'a, url, etc.) to to musi rzucić wyjątek, bo wypada. Czasem nie. Powinno się robić to co ma sens, i dla mnie to że chcesz kumulować "nie udanie się czegoś', to jest bardzo silny sygnał że wyjątki są nienajlepsze do tego. Zwykła List<String> by się lepiej nadała do tego, którą możesz normalnie zwrócić.

Ad. 3 Jeśli faktycznie zmierzysz że są bottleneckiem, to je wywal. Poza tym, to jest kolejny argument o nadużywaniu narzędzia. Ten argument sam w sobie nic nie znaczy, każde narzędzie można nadużyć. Jakbym chciał to mógłbym zrobić Either<String, Either<Either<Integer,Either<String,Boolean>,User>>>>>, i też ktoś mógłby krytykować. Wiadomo że jak ktoś rzuca wyjątek 10000x na sekundę to robi coś źle; ale to nie jest argument przeciwko wyjątkom, tylko przeciwko ich nieodopowiedniemu użyciu.

Ad. 4. Czyli składnia? Mi to wszystko jedno, sam pokazałeś jak łatwo można zmienić try/catch na funkcyjne Try().

1
Riddle napisał(a):

Ad. 3 Jeśli faktycznie zmierzysz że są bottleneckiem, to je wywal. Poza tym, to jest kolejny argument o nadużywaniu narzędzia. Ten argument sam w sobie nic nie znaczy, każde narzędzie można nadużyć. Jakbym chciał to mógłbym zrobić Either<String, Either<Either<Integer,Either<String,Boolean>,User>>>>>, i też ktoś mógłby krytykować. Wiadomo że jak ktoś rzuca wyjątek 10000x na sekundę to robi coś źle; ale to nie jest argument przeciwko wyjątkom, tylko przeciwko ich nieodopowiedniemu użyciu.

Jeśli wyjątki są normalną częścią API to niejako dopuszczamy, że ktoś to będzie czasem powodował ich 10000 na sekundę.(podany wyżej przykład parseInt to obrazuje).

0
jarekr000000 napisał(a):

Jeśli wyjątki są normalną częścią API to niejako dopuszczamy, że ktoś to będzie czasem powodował ich 10000 na sekundę.(podany wyżej przykład parseInt to obrazuje).

Nie kupuję tego argumentu.

Tak samo mógłbyś powiedzieć o wszystkich innych elementach API, gc(), new Socket(), new Thread(), File.close(), .write(), .clone(), .sort(), i powiedzieć że "skoro są normalne, to dopuszczamy że ktoś ich może wywołać 10000x na sekundę".

Każde jedno narzędzie i funkcja, ma swoje wady i zalety. Nie ma narzędzi które są dobre do wszystkiego i działają zawsze i wszędzie. Każdego narzędzia można nadużyć, i krytykować wynik.

0
Riddle napisał(a):

Ad. 1. Ukryty, owszem, ale przynajmniej nie masz Result/Either z generykami rozwalonego po kodzie wszędzie.

Zamieniasz Either na Future/IO i masz reaktywny kod. Jak już umiesz pracować z Either to jesteś w połowie drogi żeby móć wpisać sobie do CV iż znasz się na programowaniu reaktywny,

Najgorszy case to jak ten Either jest z jakiejś biblioteki, to wtedy ciężko od niej odejść/zmienić, twarda zależność.

z vavra? Jedna biblioteka z utilsami to taki koszmar? Czyli jak kiedyś wezmą Either do biblioteki standardowej to to już nie bedzie problem?

BTW to kolekcji z vavra też nie używasz bo to twarda zależność? masz jakąś definicję kiedy można użyć biblioteki zewnętrznej?

0
KamilAdam napisał(a):

Najgorszy case to jak ten Either jest z jakiejś biblioteki, to wtedy ciężko od niej odejść/zmienić, twarda zależność.

z vavra? Jedna biblioteka z utilsami to taki koszmar? Czyli jak kiedyś wezmą Either do biblioteki standardowej to to już nie bedzie problem?

Nie koszmar, tylko kalkulacja wad i zalet. Nałożenie swojej abstrakcji często jest tańsze i bardziej pragmatyczne niż rozsianie jej po aplikacji. Jak się okaże że taniej jest użyć bezpośrednio to użyję - jak uznam że nie, to schowam do swojej abstrakcji. To po prostu pragmatyzm.

KamilAdam napisał(a):

BTW to kolekcji z vavra też nie używasz bo to twarda zależność?

Staram się ograniczać poziom do jakiego zewnętrzne biblioteki lub nawet elementy ze standardowej biblioteki panoszą się po kodzie.

Jeśli mam jedno/dwa miejsca, to użyję kolekcji z vavra normalnie. Ale jesli przekazuje jakieś kolekcje pomiędzy wieloma klasami albo wieloma warstwami, to zaenkapsuluję je w coś.

0

Jak mawia jeden z wielkich programistów:

Wyjątek łatwo rzucić a ciężko obsłużyć.

Rzucanie wyjątków to przerzucanie odpowiedzialności (i dodatkowej pracy) na klientów naszego kodu. Najlepiej zrobić tak żeby tych wyjątków było jak najmniej.

Checked Exception są dodatkowo bardzo niewygodne w stosowaniu:

  1. Albo wrapujemy ten wyjątek w jakiś ogólny AppException i w takim wypadku checked nie ma sensu bo 90% będzie miało throws AppException
  2. Albo każda metoda ma swój zestaw wyjątków i tworzy to potworne zależności, dodajemy nowy typ wyjątku i potem trzeba go obsłużyć we wszystkich miejscach (to jest dobre). Tyle że jak go obsłużymy? Albo zwrapujemy (czyli wpadamy w (1)) albo dodamy nowy typ wyjątku w metodzie wołającej (czyli wpadamy w (2) i rekruencyjnie musimy dodawać nowy typ wyjątku do każdej sygnatury).

Pewnym rozwiązaniem jest zdefionwanie globalnego zestawu wyjątków i powiedzenie że aplikacja może korzystać tylko z nich. Jest to półśrodek który jako tako działa zwłaszcza gdy pewne typy wyjątków możemy chcieć obsługiwać np. HttpRequestFailedException w kodzie do retry. Zazwyczaj takich typów będzie góra 20 na setki i tysiące metod. Tyle że wtedy throws w sygnaturze traci na znaczeniu bo mapujemy wiele różnych sytuacji na ten sam typ wyjątku np. IllegalArgException raz znaczy że int jest ujemny, raz że indeks jest poza tablicą a raz że nazwa pliku zawiera złe znaki. Czyli robi się misz masz.

0

Obsługa błędów wyjątkami jak masz je obsłużyć to jakiś koszmar, zwracanie błędów też koszmar, najlepiej jakby każdy obiekt jeśli dostanie optional nulla po prostu nic nie robił, najwyżej zaloggował do stderr czy gdzieś błąd i zwrócił też nulla.

Jak się robi mega high level abstrakcje to ta obsługa błędów niszczy wszystko, bo jednak trzeba pamiętać o sprawdzeniu błędów, musisz sprawdzić dokumentację tam widzisz, że jak takie i takie coś zwróci to musisz przerwać wykonanie.

A mogło by być tak, że nawet jak błąd wystąpi to nic nie trzeba robić samo się wszystko obsłuży, bo teraz powiedzmy chcemy zrobić taki prosty system drag and drop do obsługi naszych bibliotek to nic nie powinno blokować przepływu danych czy sprawiać, że musimy jakieś błędy sprawdzać, po prostu jak obiekt jest zwrócony optional null to po prostu inne go ignorują i też nullują, a jedynie każdy reportuje błąd.

Ja bym tak podszedł do wszystkiego, bo mam obiekt i coś się nie uda, to muszę jakoś ten błąd zakomunikować, potem go obsłużyć, a mogło by być, że nie trzeba w ogóle przepływu danych zmieniać.
Mnie tam wszystko denerwuje.

0
.GodOfCode. napisał(a):

Obsługa błędów wyjątkami jak masz je obsłużyć to jakiś koszmar, zwracanie błędów też koszmar, najlepiej jakby każdy obiekt jeśli dostanie optional nulla po prostu nic nie robił, najwyżej zaloggował do stderr czy gdzieś błąd i zwrócił też nulla.

Jak się robi mega high level abstrakcje to ta obsługa błędów niszczy wszystko, bo jednak trzeba pamiętać o sprawdzeniu błędów, musisz sprawdzić dokumentację tam widzisz, że jak takie i takie coś zwróci to musisz przerwać wykonanie.

A mogło by być tak, że nawet jak błąd wystąpi to nic nie trzeba robić samo się wszystko obsłuży, bo teraz powiedzmy chcemy zrobić taki prosty system drag and drop do obsługi naszych bibliotek to nic nie powinno blokować przepływu danych czy sprawiać, że musimy jakieś błędy sprawdzać, po prostu jak obiekt jest zwrócony optional null to po prostu inne go ignorują i też nullują, a jedynie każdy reportuje błąd.

Ja bym tak podszedł do wszystkiego, bo mam obiekt i coś się nie uda, to muszę jakoś ten błąd zakomunikować, potem go obsłużyć, a mogło by być, że nie trzeba w ogóle przepływu danych zmieniać.
Mnie tam wszystko denerwuje.

WTF? 👀

Mi się zdaje, czy Pan nie wiesz o czym Pan mówisz?

0

Wie o czym mówi, coś ala: https://en.wikipedia.org/wiki/Null_object_pattern

Wtedy null jest też instancja obiektu, w null-aware-Java:

User u = null; // smart null
u.getName() // zwraca smart null
u.getName().indexOf('foo') // zwraca smart null

Coś ala nested stubs w Mockito.

Wedy jak masz taki "smart null" to można wołać wszystkie metody na obiekcie, a w wyniki zawsze dostaniesz inny smart null.

Tyle że nie znam języka co by to w pełni implementował...

2

No tylko to jest słabe, bo gdzieś na górze dostajesz taki NullObject, i nie wiadomo czemu jest nullem - która operacja w chainie zawiodła.

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