Obsługa wyjątków w waszej pracy

0

Cześć wszystkim,
zdaję sobie sprawę, że to temat bardziej do Inżynierii Oprogramowania, niemniej jednak pozwoliłem sobie go umieścić w tym dziale, ponieważ nie interesuje mnie w żadnym wypadku jak to wygląda w innych niż C++ językach.

Pytanie jest następujące: Jak dużą uwagę poświęcacie w pracy na obsługę wyjątków? U mnie w firmie jest tego mnóstwo, praktycznie dla każdej metody pisany jest try-catch nawet jeżeli to zwykły setter. Z drugiej strony, w prywatnych projektach, praktycznie wcale z obsługi wyjątków nie korzystam.
Jak jest u was? Osobiście uważam, że najlepsze rozwiązanie jest gdzieś po środku, czyli obsługujemy wyjątki ale tylko wtedy, gdy ma to faktycznie sens.

4

To jest bezsensu.
Po to są wymyślono wyjątki by błąd się propagował przez kod, bez pisania usługi błędów na każdym poziomie.
Po pierwsze używaj RAII wszędzie gdzie się da, zwykle to bardzo skutecznie podczyszcza try - catch i jest pewne że nie będzie wycieku zasobów.
A wyjątki obsługuj tylko w tym miejscu, gdzie faktycznie coś można z nimi sensownego zrobić.

Jeśli zwykły setter ma try-catch to coś jest nie tak z programistą, który to napisał (chyba, że różnie rozumiemy "zwykły setter").

1

Wydaje mi się, że warto odróżnić to co faktycznie jest wyjątkiem (wyjątkowa sytuacja: brak połączenia z internetem, z bazą itp), a to co jest tylko normalnym błędem (brak zasobu pod podanym id, brak uprawnień itp). Pierwsze czyli wyjątki, można normalnie obsługiwać wyjątkami (od tego w końcu są), natomiast rzucanie przy zwykłym błędzie jest taką trochę drogą na skróty, takim ukrytym GOTO.

Acz mówię z perspektywy javy.

0

Ja tam wolę konsekwencję, jak wyjątki to wyjątki na wszystkie błędy. Jak bez wyjątków to bez wyjątków. To czasami jest trudne do zrealizowania, bo jakaś zależność rzuca wyjątki, a inna zwraca kody błędu, ale wtedy zależność opakowuję i dążę do jednolitego zachowania.

W tej materii popieram opinię Robert C. Martin'a (autor Clean Code, pisze o Java, ale stosuje się wszędzie):

  • błąd -> wyjątek
  • nigdy nie zwracać NULL-a, by nie trzeba było sprawdzać czy null
  • dany moduł powinien mieć swoją klasę wyjątków
  • dany moduł powinien łapać wyjątki, które do niego przychodzą od jego zależności i opakowywać go swoim wyjątkiem

Przy czym zaznaczam (tak jak on), że to jest tylko jego opinia i jego praktyka, którą uważa za sprawdzoną.

1
MarekR22 napisał(a):
  • błąd -> wyjątek

No i tu zazwyczaj zaczyna się dylemat. Bo co jest błędem, a co zwykłą możliwością wynikającą z logiki biznesowej? Takie zwykłe sytuacje można obsługiwać przez opakowanie zwracanej wartości w jakiegoś Either czy podobną strukturę i wtedy masz zwróconą informacje o błędzie i nie ma żadnego magicznego skakania po kodzie

2

C++ to nie moja bajka, jednak piszę w języku o zbliżonym poziomie abstrakcji, więc co nieco ogólnie.

Tenonymous napisał(a):

Pytanie jest następujące: Jak dużą uwagę poświęcacie w pracy na obsługę wyjątków?

Dużą, dlatego że wyjątki są i będą rzucane, więc trzeba je łapać i obsługiwać.

Nie można też dopuścić do sytuacji, w której wyjątku się nie złapie w odpowiednim miejscu i nie obsłuży, bo to może zaowocować np. wyciekami pamięci lub zdestabilizowaniem obiektu. Nie można też pozwolić na to, aby wyjątek w ogóle nie został złapany. Bo wstyd, gdy użytkownik dostanie w twarz komunikatem w stylu Access Violation at address ….

U mnie w firmie jest tego mnóstwo, praktycznie dla każdej metody pisany jest try-catch nawet jeżeli to zwykły setter.

Widziałem, używałem i używam setterów rzucających wyjątki, ale nie pamiętam, abym musiał je łapać i obsługiwać w setterze. Jeśli dane wejściowe są niepoprawne (luźny przykład: indeks spoza zakresu), to odpowiednio reaguje się (lub nie, zależy od sytuacji), a następnie tworzy i rzuca wyjątek – niech sobie frunie, bo gdzieś ”wyżej” jest łapany i obsługiwany oraz ew. puszczany dalej, jeśli sytuacja tego wymaga.

Inna sprawa to sytuacja, w której ktoś zamiast skorzystać z prostej instrukcji warunkowej do obsługi sytuacji wyjątkowej (nie wyjątku), tworzy blok try, w nim warunkiem waliduje dane wejściowe i rzuca wyjątek tylko po to, aby go w tym samym bloku złapać i obsłużyć. Bez komentarza…

Jak jest u was?

Elastycznie jest, bo świat nie jest czarno-biały.

Najważniejsze jest to, aby utrzymywać jak najwyższą czytelność kodu i aby reagować na każdy możliwy błąd w odpowiedni sposób. Propagacja wyjątków musi być prawidłowa, tak samo jak przepływ sterowania, do czego się zawsze dąży. Dla mnie dobry kod to taki, który zabezpiecza wszystkie przypadki, stąd zabawa w „co jeśli…?” jest na porządku dziennym.

Czasem zdarza mi się ”przyjanuszować”, np. używając Pokemon Exception Handling, jednak jeśli w danym bloku catch (u mnie except) należy wykonać dokładnie te same czynności bez względu na klasę wyjątku, to co mi szkodzi. Taki sposób jest krótki i jednoznaczny, więc każdy zrozumie.


Tyle że ze mną jest trochę inaczej, bo nie boję się używać konstrukcji powszechnie napiętnowanych, skoro w danej sytuacji taka konstrukcja pozwala zwiększyć czytelność kodu i ułatwić jego utrzymanie. To trochę jak z goto – nie używam, jednak samo jego istnienie mi nie przeszkadza, jeśli dzięki niemu łatwiej zrozumieć o co chodzi. ;)

0

To co, jak skanujesz plik i wchodzi linia z 3 znakami zamiast 4 to wszystko ma sie zawalic i terminate()?
Jaki system by cos takiego wytrzymal. Moze twoja konsolowa applikacja z 300 liniami kodu ale nie cos komercyjnego.

3
Lexik napisał(a):

To co, jak skanujesz plik i wchodzi linia z 3 znakami zamiast 4 to wszystko ma sie zawalic i terminate()?

Brak obsługi wyjątków nie oznacza braku obsługi błędów i walidacji danych. W całym kodzie projektu może nie być w ogóle żadnego bloku odpowiedzialnego za łapanie i obsługę wyjątków, co wcale nie oznacza, że pierwszy lepszy błąd spowoduje ubicie procesu.

Dawno temu wyjątków nie było, a mimo to błędy były wyłapywane i obsługiwane. Kto pisał np. w C lub w czystym WinAPI ten wie o co chodzi. Zwracanie kodów błędów przez funkcje czy namiętne używanie GetLastError było standardową techniką, niczym nadzwyczajnym.

Lexik napisał(a):

jak ze wzgledow wydajnosciowych, no jak??? Bosze, przeciez, wyjatek jak sama nazwa mowie to jest wyjatek a nie regula.

Obsługa wyjątków jest znacznie bardziej skomplikowana i czasochłonna niż np. prosty test ifem i zwrócenie kodu błędu w rezultacie funkcji. Co za tym idzie, nieużywanie wyjątków jak najbardziej wpływa na efektywność, w stopniu zależnym od przypadku (choć wąskim gardłem raczej być nie powinny).

No chyba, ze masz tak, ze kod wywala wyjatki non-stop.

Tu nie chodzi o sam fakt rzucania wyjątków i o częstotliwość ich rzucania, a za generowanie przez kompilator kodu odpowiedzialnego za ich rzucanie, propagację, wyłapywanie i obsługę.

Jak mozna pisac applikacje bez stosowania wyjatkow?

Można. Nawet więcej – można używać mechanizmu wyjątków i jednocześnie go nie używać! ;)

2
furious programming napisał(a):

Obsługa wyjątków jest znacznie bardziej skomplikowana i czasochłonna niż np. prosty test ifem i zwrócenie kodu błędu w rezultacie funkcji. Co za tym idzie, nieużywanie wyjątków jak najbardziej wpływa na efektywność, w stopniu zależnym od przypadku (choć wąskim gardłem raczej być nie powinny).

Na jakimś cppcon prelegent mówił, że to jest już przeszłość. Przy braku błędów nie ma narzutu, a w przypadku błędu jest jakiś narzut.
Zresztą znalazłem ładną odpowiedź na SO na ten temat: https://stackoverflow.com/a/13836329/1387438

0
MarekR22 napisał(a):

Przy braku błędów nie ma narzutu, a w przypadku błędu jest jakiś narzut.

Nigdzie nie pisałem, że przy braku błędów narzut istnieje (co i tak jest zależne od kompilatora). Po prostu stwierdzenie, że wyjątki nie wpływają na efektywność działania kodu jest nieprawidłowe. To fakt, optymaliwanie kodu przez wyłączenie obsługi wyjątków brzmi nieco dziwnie, choć w jakimś tam specyficznym przypadku może to mieć jakieś znaczenie.

Owym narzutem nie ma sensu się przejmować, by jest znikomy, a już na pewno nie będzie wąskim gardłem. Chyba że źródła są wykastrowane z instrukcji warunkowych, a wszelką walidację danych opiera się właśnie o wyjątki, jak tu gość w komentarzu wyżej przykład podał.

Do bulwersu z powodu nieużywania wyjątków w całym projekcie też nie ma zbytnio powodu, bo przecież wszystko zależy od specyfiki projektu i narzuconych (obowiązujących) reguł.

0

-fno-exceptions

0

W większości projektów mam zabronione używanie wyjątków. Nie ze względu na to że "som wolne one" a ze względu na niepewność specjalizowanego kompilatora który może mieć błędy na platformie nie-x86 lub wymagania procesu.
Nie są złe, powstały w jakimś celu. Jednym z powodów (tu nie padł), jest łatwa obsługa błędu z funkcji (lub innej jednostki kodu) która nie jest totalna. Czyli taka, która dla całego zakresu typów argumentów zwraca poprawne wartości. Po pierwsze implementacja typu jest ograniczona jego reprezentacją, po drugie nie każdy będzie chciał definiować specjalizowany typ który będzie zachowywał totalność funkcji, po trzecie wartość w danym typie z różnych powodów (zewnętrznych) okazuje się nieprawidłowa.
Stąd (IMHO) pragmatyczny rodowód wyjątku :)
Innym remedium na totalność jest std::optional i std::variant. Znów.. niepełnym.
Moim zdaniem warto uwzględniać te uwarunkowania także. To one wpływają na dobre praktyki.

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