Obsługa błędów - jeśli program zawiedzie w wyniku błędów lub po cichu je zignoruje


20

Piszę prosty, mały program do przesyłania MIDI przez sieć. Wiem, że program napotka problemy z transmisją i / lub inne wyjątkowe sytuacje, których nie będę w stanie przewidzieć.

W przypadku obsługi wyjątków widzę dwa podejścia. Czy powinienem napisać program, aby:

  • zawiedzie z hukiem, gdy coś pójdzie nie tak lub
  • czy powinien po prostu zignorować błąd i kontynuować, kosztem integralności danych?

Którego podejścia użytkownik mógłby się spodziewać?
Czy istnieje lepszy sposób obsługi wyjątków?

Ponadto, czy na moją decyzję dotyczącą obsługi wyjątków powinien mieć wpływ to, czy mam do czynienia z połączeniem sieciowym (tj. Czymś, co do którego można zasadnie oczekiwać, że pojawią się problemy)?


1
@ GlenH7 Pay it forward ... :)
komiks

Czy istnieje sposób na edycję +1? ;) Och, czekaj, idę za tobą. Pewnie, zrobi :)
Arlen Beiler

2
„Którego podejścia użytkownik mógłby się spodziewać?”. Czy próbowałeś zapytać jednego ze swoich użytkowników? Testy użyteczności na korytarzu mogą być niezwykle skuteczne. Zobacz blog.openhallway.com/?p=146
Bryan Oakley

Nie mam żadnych użytkowników :)
Arlen Beiler

Odpowiedzi:


34

Nigdy nie należy ignorować błędu napotkanego przez program. Jako minimum powinieneś zalogować go do pliku lub innego mechanizmu w celu powiadomienia. Nie może być sporadyczne sytuacje, w których będziemy chcieli zignorować błąd ale udokumentować to! Nie pisz pustego catchbloku bez komentarzy wyjaśniających, dlaczego jest pusty.

To, czy program powinien zawieść, czy nie, zależy w dużej mierze od kontekstu. Jeśli możesz poradzić sobie z błędem z wdziękiem, idź do niego. Jeśli jest to nieoczekiwany błąd, nastąpi awaria programu. To właściwie podstawowa obsługa wyjątków.


18
Nie wystarczy, aby uzasadnić -1, ale „nigdy” jest mocnym słowem, i są sytuacje, w których ignorowanie błędów jest właściwym działaniem. Na przykład oprogramowanie do dekodowania transmisji HDTV. Gdy napotkasz jakieś błędy, które często napotykasz, wszystko, co możesz zrobić, to zignorować je i nadal dekodować, co nastąpi później.
whatsisname

1
@whatsisname: prawda, ale powinieneś wyraźnie udokumentować, dlaczego ją ignorujesz. Zaktualizowana odpowiedź w celu rozważenia Twojego komentarza.
marco-fiset

1
@ marco-fiset Nie zgadzam się. Awaria całego programu jest rozwiązaniem tylko wtedy, gdy ponowne uruchomienie go rozwiązałoby problem. Nie zawsze tak jest, więc zawieszanie się jest często bezcelowe, a domyślne zachowanie jest po prostu głupie. Dlaczego chcesz zawiesić całą operację z powodu zlokalizowanego błędu? To nie ma sensu. Większość aplikacji to robi iz jakiegoś powodu programiści mają się z tym dobrze.
MaiaVictor,

@Dokkat: chodzi o to, aby nie kontynuować z niewłaściwymi wartościami, co da złe rozwiązanie (wyrzucanie śmieci, wyrzucanie śmieci). Awaria naprawia problem: zmusza użytkownika do podania różnych danych wejściowych lub zmarnowania programisty na
naprawę

1
@whatsisname Myślę, że w takim przypadku możesz pomyśleć o swojej definicji „błędu”. Błędy w otrzymanych danych to nie to samo, co błędy w strukturze i działaniu programu.
joshin4colours 15.03.13

18

Nigdy nie powinieneś cicho ignorować błędów, ponieważ twój program opiera się na szeregu działań, które domyślnie zależą od wszystkiego, co minęło, zanim poszły dobrze. Jeśli coś pójdzie nie tak w kroku 3 i spróbujesz przejść do kroku 4, krok 4 rozpocznie się na podstawie nieprawidłowych założeń, co zwiększa prawdopodobieństwo, że również spowoduje błąd. (A jeśli to zignorujesz, to krok 5 generuje błąd i stamtąd zaczyna się śnieżka.)

Chodzi o to, że w miarę narastania błędów, w końcu natrafisz na błąd tak duży, że nie możesz go zignorować, ponieważ będzie on polegał na tym, że użytkownik otrzyma coś i że coś będzie całkowicie nie tak. Następnie użytkownicy narzekają na ciebie, że twój program nie działa poprawnie i musisz to naprawić. A jeśli część „daj coś użytkownikowi” znajduje się w kroku 28, a nie masz pojęcia, że ​​pierwotny błąd powodujący cały ten bałagan był w kroku 3, ponieważ zignorowałeś błąd w kroku 3, będziesz mieć cholera, czas debugowania problemu!

Z drugiej strony, jeśli ten błąd w kroku 3 powoduje wysadzenie wszystkiego w twarz użytkownika i generuje błąd mówiący SOMETHING WENT BADLY WRONG IN STEP 3!(lub jego techniczny odpowiednik, ślad stosu), to wynik jest taki sam - użytkownik narzeka na ciebie program nie działa poprawnie - ale tym razem wiesz dokładnie, od czego zacząć szukać, kiedy go naprawić .

EDYCJA: W odpowiedzi na komentarze, jeśli coś pójdzie nie tak, jak się spodziewałeś i wiesz, jak sobie z tym poradzić, jest inaczej. Na przykład w przypadku otrzymania źle sformułowanego komunikatu nie jest to błąd programu; to „użytkownik podał błędne dane wejściowe, których nie udało się sprawdzić”. Odpowiednią odpowiedzią jest poinformowanie użytkownika, że ​​przekazuje ci nieprawidłowe dane wejściowe, co brzmi jak robisz. Nie ma potrzeby zawieszania się i generowania śladu stosu w takim przypadku.


Co powiesz na powrót do ostatniej znanej dobrej pozycji lub, w przypadku pętli odbiorczej, po prostu upuść tę wiadomość i przejdź do następnej.
Arlen Beiler

@Arlen: Nawet to może być zły pomysł. Występują błędy, ponieważ wydarzyło się coś, czego nie spodziewałeś się podczas kodowania . Oznacza to, że wszystkie twoje założenia zniknęły za oknem. Ta „ostatnia znana dobra pozycja” może już nie być dobra, gdybyś był w połowie modyfikacji struktury danych, a teraz jest na przykład niespójny.
Mason Wheeler

Co jeśli się tego spodziewam, na przykład uszkodzone wiadomości zapobiegające deserializacji. W niektórych przypadkach serializowałem wyjątek i wysyłałem go z powrotem przez sieć. Tam zobacz moją edycję pytania.
Arlen Beiler

@Arlen: Jeśli to przewidziałeś i jesteś pewien, że wiesz, jakie są skutki błędu i że są one odpowiednio zawarte, to jest inaczej. Wydaje mi się, że radzisz sobie z błędem i odpowiednio reagujesz, nie ignorując go. Mówiłem o ignorowaniu nieoczekiwanych błędów, a brzmiało to tak, jakbyś pytał.
Mason Wheeler

16

Istnieją inne opcje między „wysadzeniem w powietrze” a „zignorowaniem”.

Jeśli błąd jest przewidywalny i można go uniknąć, zmień projekt lub zmodyfikuj kod, aby go uniknąć.

Jeśli błąd jest przewidywalny, ale nie da się go uniknąć, ale wiesz, co zrobić, gdy się zdarzy, złap go i poradz sobie z sytuacją. Uważaj jednak, aby nie używać wyjątków jako kontroli przepływu. W tym momencie możesz chcieć zapisać ostrzeżenie i być może powiadomić użytkownika, jeśli podejmie jakieś działania, aby uniknąć tej sytuacji w przyszłości.

Jeśli błąd jest przewidywalny, nieunikniony, a gdy się zdarzy, nic nie można zrobić, co zagwarantuje integralność danych, musisz zarejestrować błąd i wrócić do bezpiecznego stanu (który, jak powiedzieli inni, może oznaczać awarię).

Jeśli błąd nie jest czymś, czego się spodziewałeś, to naprawdę nie możesz być pewien, że możesz nawet wrócić do bezpiecznego stanu, więc najlepiej po prostu zalogować się i zawiesić.

Zasadniczo nie wychwytuj żadnych wyjątków, z którymi nie możesz nic zrobić, chyba że po prostu planujesz zalogować się i ponownie wrzucić. A w rzadkich przypadkach, gdy nie można uniknąć try-catch-ignore, dodaj przynajmniej komentarz w bloku catch, aby wyjaśnić, dlaczego.

Zobacz doskonały artykuł Erica Lipperta na temat obsługi wyjątków, aby uzyskać więcej sugestii dotyczących kategoryzacji i obsługi wyjątków.


1
Najlepsza odpowiedź do tej pory, moim zdaniem.
Czwartek,

6

Oto moje poglądy na pytanie:

Dobrą zasadą początkową jest szybkie zawodzenie. W szczególności nigdy nie należy pisać kodu obsługi błędów w przypadku awarii, dla których nie znasz dokładnej przyczyny.

Po zastosowaniu tej zasady możesz dodać kod odzyskiwania dla określonych napotkanych błędów. Możesz także wprowadzić kilka „bezpiecznych stanów”, do których możesz wrócić. Przerwanie programu jest w większości bezpieczne, ale czasami możesz chcieć wrócić do innego znanego dobrego stanu. Przykładem jest, w jaki sposób nowoczesny system operacyjny obsługuje szkodliwy program. Wyłącza tylko program, a nie cały system operacyjny.

Zawodząc szybko i powoli pokrywając coraz bardziej specyficzne warunki błędu, nigdy nie naruszasz integralności danych i stale przechodzisz na bardziej stabilny program.

Połknięcie błędów, tj. Próba zaplanowania błędów, dla których nie znasz dokładnej przyczyny, a zatem nie masz konkretnej strategii odzyskiwania, prowadzi tylko do coraz większej liczby pomijania błędów i obchodzenia kodu w twoim programie. Ponieważ nie można ufać, że poprzednie dane zostały poprawnie przetworzone, zaczniesz widzieć rozłożone sprawdzanie nieprawidłowych lub brakujących danych. Twoja cykliczna złożoność wymknie się spod kontroli i skończysz z wielką kulą błota.

To, czy zdajesz sobie sprawę z przypadków awarii, ma mniejsze znaczenie. Ale jeśli na przykład masz do czynienia z połączeniem sieciowym, dla którego znasz pewną liczbę stanów błędów, odłóż dodawanie obsługi błędów, aż dodasz również kod odzyskiwania. Jest to zgodne z zasadami przedstawionymi powyżej.


6

Nigdy nie powinieneś cicho ignorować błędów. A zwłaszcza nie kosztem integralności danych .

Program próbuje coś zrobić. Jeśli to się nie powiedzie, musisz zmierzyć się z tym faktem i coś z tym zrobić . To, co to będzie, zależy od wielu rzeczy.

W końcu użytkownik poprosił program, aby coś zrobił, a program powinien powiedzieć mu, że się nie udało. Jest wiele sposobów, jak to zrobić. Może zatrzymać się natychmiast, może nawet cofnąć już ukończone kroki lub, z drugiej strony, może kontynuować i wykonać wszystkie kroki, jakie może, a następnie poinformować użytkownika, że ​​te kroki się powiodły, a inne nie.

Wybór sposobu zależy od tego, jak ściśle powiązane są kroki i czy prawdopodobne jest, że błąd wystąpi ponownie dla wszystkich przyszłych kroków, co z kolei może zależeć od dokładnego błędu. Jeśli wymagana jest silna integralność danych, musisz przywrócić do ostatniego spójnego stanu. Jeśli kopiujesz tylko kilka plików, możesz pominąć niektóre i po prostu powiedzieć użytkownikowi, że tych plików nie można skopiować. Nie należy po cichu pomijać plików i informować użytkownika o niczym.

Edycja reklamy, jedyną różnicą, którą się robi, jest to, że powinieneś rozważyć kilka razy, zanim zrezygnujesz i powiesz użytkownikowi, że nie działał, ponieważ w sieci prawdopodobnie wystąpią przejściowe błędy, które nie powtórzą się, jeśli spróbujesz ponownie.


Czy naprawdę chciałeś umieścić znak zapytania na końcu pierwszego wiersza?
CVn

@ MichaelKjörling: Nie. Błąd kopiuj-wklej (skopiował formułę z pytania i pomyłkowo zawarł znak zapytania).
Jan Hudec

4

Istnieje jedna klasa przypadków, w których należy ignorować błędy: gdy nic nie można zrobić w tej sytuacji, a słabe i prawdopodobnie nieprawidłowe wyniki są lepsze niż brak wyników.

Przypadek dekodowania strumienia HDMI do celów wyświetlania jest takim przypadkiem. Jeśli strumień jest zły, jest zły, krzyczenie na ten temat nie naprawi go magicznie. Robisz, co możesz, aby je wyświetlić i pozwalasz widzowi zdecydować, czy jest to dopuszczalne, czy nie.


1

Nie sądzę, aby program po cichu ignorował lub powodował spustoszenie, ilekroć napotkał problem.

Co robię z oprogramowaniem wewnętrznym, które piszę dla mojej firmy ...

Zależy to od błędu, powiedzmy, że jeśli jest to funkcja krytyczna, która wprowadza dane do MySQL, musi poinformować użytkownika, że ​​się nie udało. Procedura obsługi błędów powinna próbować zebrać jak najwięcej informacji i dać użytkownikowi pomysł, jak samodzielnie poprawić błąd, aby mógł zapisać dane. Chciałbym również podać sposób, w jaki dyskretnie przesyłają nam informacje, które próbują zapisać, więc jeśli pogorszy się, możemy wprowadzić je ręcznie po usunięciu błędu.

Jeśli nie jest to funkcja krytyczna, coś, co może popełnić błąd i nie wpłynąć na ostateczny wynik tego, co próbują osiągnąć, mogę nie pokazać im komunikatu o błędzie, ale wysłać e-mail, który automatycznie wstawi go do naszego oprogramowania do śledzenia błędów lub e-mailowa grupa dystrybucyjna, która ostrzega wszystkich programistów w firmie, abyśmy byli świadomi błędu, nawet jeśli nie jest nim użytkownik. To pozwala nam naprawić zaplecze, podczas gdy w interfejsie nikt nie wie, co się dzieje.

Jedną z największych rzeczy, których staram się unikać, jest awaria programu po błędzie - niemożność odzyskania. Zawsze staram się dać użytkownikowi opcję kontynuowania bez zamykania aplikacji.

Wierzę, że jeśli nikt nie wie o błędzie - nigdy nie zostanie naprawiony. Jestem również zwolennikiem obsługi błędów, która umożliwia kontynuowanie działania aplikacji po wykryciu błędu.

Jeśli błąd jest związany z siecią - dlaczego nie wykonać funkcji prostego testu komunikacji sieciowej przed wykonaniem funkcji, aby w pierwszej kolejności uniknąć błędu? Po prostu ostrzegając użytkownika, że ​​połączenie jest niedostępne, sprawdź swój Internet itp. Itp. I spróbuj ponownie?


1
Osobiście przeprowadzam wiele weryfikacji przed uruchomieniem funkcji krytycznych, aby spróbować ograniczyć błędy, których nie do końca rozumiem i nie mogę zapewnić użytecznej obsługi błędów, która zwraca szczegółowe informacje. Nie działa w 100% przypadków, ale nie pamiętam, kiedy ostatni raz miałem błąd, który stracił mnie przez wiele godzin.
Jeff

1

Moja własna strategia polega na rozróżnianiu błędów kodowania (błędów) i błędów środowiska wykonawczego oraz, w miarę możliwości, utrudnia tworzenie błędów kodowania.

Błędy należy naprawić jak najszybciej, więc właściwe jest podejście według umowy . W C ++ lubię sprawdzać wszystkie moje warunki wstępne (dane wejściowe) za pomocą asercji u góry funkcji, aby jak najszybciej wykryć błąd i ułatwić dołączenie debuggera i naprawienie błędu. Jeśli zamiast tego programista lub tester zdecyduje się kontynuować działanie programu, utrata integralności danych stanie się ich problemem.

I przede wszystkim znajdź sposoby na uniknięcie błędu. Ścisłe przestrzeganie stałej poprawności i wybieranie typów danych odpowiednich do przechowywanych danych to dwa sposoby utrudniające tworzenie błędów. Fail-Fast jest również dobry poza kodem krytycznym dla bezpieczeństwa, który potrzebuje sposobu na odzyskanie.

W przypadku błędów środowiska wykonawczego, które mogą wystąpić w przypadku kodu wolnego od błędów, takich jak awarie komunikacji sieciowej lub szeregowej albo brakujące lub uszkodzone pliki:

  1. Zaloguj błąd.
  2. (Opcjonalnie) Spróbuj po cichu ponowić próbę lub w inny sposób przywrócić działanie po operacji.
  3. Jeśli operacja nadal kończy się niepowodzeniem lub jest niemożliwa do odzyskania, zgłoś błąd widocznie użytkownikowi. Następnie, jak wyżej, użytkownik może zdecydować, co robić. Pamiętaj o zasadzie najmniejszego zdziwienia , ponieważ utrata integralności danych zaskakuje użytkownika, chyba że wcześniej go ostrzeżesz.

0

Niepowodzenie jest właściwą opcją, gdy masz powody, by sądzić, że ogólny stan programu jest niestabilny i może się wydarzyć coś złego, jeśli pozwolisz mu odtąd działać. W pewnym sensie „ignorowanie” (tj., Jak zauważyli inni, rejestrowanie go gdzieś lub wyświetlanie użytkownikowi komunikatu o błędzie, a następnie kontynuowanie) jest w porządku, gdy wiadomo, że z pewnością nie można wykonać bieżącej operacji, ale program może Biegnij dalej.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.