Najlepsze praktyki dotyczące zarządzania wyjątkami w Javie lub C # [zamknięte]

117

Utknąłem przy podejmowaniu decyzji, jak obsłużyć wyjątki w mojej aplikacji.

Wiele, jeśli moje problemy z wyjątkami wynikają z 1) dostępu do danych za pośrednictwem usługi zdalnej lub 2) deserializacji obiektu JSON. Niestety nie mogę zagwarantować sukcesu dla żadnego z tych zadań (przerwanie połączenia sieciowego, źle sformułowany obiekt JSON, który jest poza moją kontrolą).

W rezultacie, jeśli napotkam wyjątek, po prostu łapię go w funkcji i zwracam FALSE do dzwoniącego. Moja logika jest taka, że ​​dzwoniącego naprawdę obchodzi tylko to, czy zadanie się powiodło, a nie dlaczego się nie powiodło.

Oto przykładowy kod (w języku JAVA) typowej metody)

public boolean doSomething(Object p_somthingToDoOn)
{
    boolean result = false;

    try{
        // if dirty object then clean
        doactualStuffOnObject(p_jsonObject);

        //assume success (no exception thrown)
        result = true;
    }
    catch(Exception Ex)
    {
        //don't care about exceptions
        Ex.printStackTrace();
    }
    return result;
}

Myślę, że to podejście jest w porządku, ale jestem naprawdę ciekawy, jakie są najlepsze praktyki zarządzania wyjątkami (czy naprawdę powinienem bąbelkować wyjątek na całej wysokości stosu wywołań?).

Podsumowując kluczowe pytania:

  1. Czy można po prostu wyłapać wyjątki, ale nie podświetlać ich ani formalnie powiadamiać systemu (za pośrednictwem dziennika lub powiadomienia dla użytkownika)?
  2. Jakie sprawdzone metody są dostępne dla wyjątków, które nie powodują, że wszystko wymaga blokady try / catch?

Śledzenie / edycja

Dziękuję za wszystkie opinie, znalazłem doskonałe źródła dotyczące zarządzania wyjątkami w Internecie:

Wydaje się, że zarządzanie wyjątkami jest jedną z tych rzeczy, które różnią się w zależności od kontekstu. Ale co najważniejsze, należy konsekwentnie zarządzać wyjątkami w systemie.

Dodatkowo uważaj na gnicie kodu poprzez nadmierne try / catch lub nie nadawanie wyjątku jego szacunku (wyjątkiem jest ostrzeżenie systemu, co jeszcze należy ostrzec?)

Jest to również dobry komentarz ze strony m3rLinEz .

Zgadzam się z Andersem Hejlsbergiem i Tobą, że większości dzwoniących zależy tylko na tym, czy operacja się powiedzie, czy nie.

Z tego komentarza nasuwa się kilka pytań, o których należy pomyśleć podczas radzenia sobie z wyjątkami:

  • Jaki jest sens zgłaszania tego wyjątku?
  • Jak to ma sens, aby sobie z tym poradzić?
  • Czy dzwoniącego naprawdę obchodzi wyjątek, czy po prostu obchodzi go, czy połączenie się powiodło?
  • Czy zmuszanie dzwoniącego do zarządzania potencjalnym wyjątkiem jest wdzięczne?
  • Czy szanujesz idomy języka?
    • Czy naprawdę musisz zwrócić flagę sukcesu, taką jak wartość logiczna? Zwracanie wartości logicznej (lub int) jest bardziej nastawieniem na język C niż w Javie (w Javie wystarczyłoby obsłużyć wyjątek).
    • Postępuj zgodnie z konstrukcjami zarządzania błędami związanymi z językiem :)!
AtariPete
źródło
Chociaż artykuł ze społeczności oracle jest napisany w Javie, jest to ogólna rada dla bardzo szerokiej klasy języków. Ładny artykuł, właśnie tego szukałem.
Bunny Rabbit

Odpowiedzi:

61

Wydaje mi się dziwne, że chcesz wychwycić wyjątki i przekształcić je w kody błędów. Jak myślisz, dlaczego dzwoniący wolałby kody błędów od wyjątków, skoro ten drugi jest domyślny zarówno w Javie, jak i C #?

Jeśli chodzi o Twoje pytania:

  1. Powinieneś łapać tylko wyjątki, z którymi możesz sobie poradzić. W większości przypadków samo wyłapywanie wyjątków nie jest właściwe. Istnieje kilka wyjątków (np. Wyjątki dotyczące rejestrowania i organizowania między wątkami), ale nawet w takich przypadkach należy generalnie ponownie zgłosić wyjątki.
  2. Zdecydowanie nie powinieneś mieć w swoim kodzie wielu instrukcji try / catch. Ponownie, chodzi o to, aby wyłapać tylko wyjątki, z którymi możesz sobie poradzić. Możesz dołączyć najwyższą procedurę obsługi wyjątków, aby zamienić nieobsłużone wyjątki w coś użytecznego dla użytkownika końcowego, ale w przeciwnym razie nie powinieneś próbować przechwytywać każdego wyjątku w każdym możliwym miejscu.
Brian Rasmussen
źródło
Co do tego, dlaczego ktoś miałby chcieć kody błędów zamiast wyjątków ... Zawsze myślałem, że to dziwne, że HTTP nadal używa kodów błędów, mimo że moja aplikacja generuje wyjątek. Dlaczego protokół HTTP nie może pozwolić mi przekazać wyjątku tak, jak jest?
Trejkaz,
najlepsze, co możesz zrobić, to opakować kody błędów w
monadę
@Trejkaz Nie chcesz zwracać użytkownikom szczegółów wyjątku, ponieważ jest to zagrożenie bezpieczeństwa. Dlatego serwery HTML zwracają kody błędów. Zwracanie komunikatu o błędzie jest również problemem związanym z lokalizacją i prawdopodobnie powoduje, że kod HTML jest większy i wolniejszy w zwracaniu. Dlatego uważam, że serwery HTML zwracają kody błędów.
Didier A.
Myślę, że lepiej powiedzieć: „Powinieneś pomijać tylko wyjątki, z którymi faktycznie sobie poradzisz”.
Didier A.
@didibus Dlaczego uważasz, że obawy związane z bezpieczeństwem powinny powodować niedogodności w rozwoju? Nigdy nie mówiłem nic o produkcji. Jeśli chodzi o lokalizację komunikatów o błędach, cała witryna internetowa ma już ten problem i wydaje się, że ludzie sobie z tym radzą.
Trejkaz
25

Zależy to od zastosowania i sytuacji. Jeśli budujesz komponent biblioteki, powinieneś zgrupować wyjątki, chociaż powinny one być opakowane, aby były kontekstowe z komponentem. Na przykład, jeśli budujesz bazę danych XML i powiedzmy, że używasz systemu plików do przechowywania danych, a do zabezpieczenia danych używasz uprawnień systemu plików. Nie chciałbyś wyświetlać wyjątku FileIOAccessDenied, ponieważ wycieka on z implementacji. Zamiast tego zawinąłbyś wyjątek i zgłosił błąd AccessDenied. Jest to szczególnie ważne, jeśli udostępniasz komponent stronom trzecim.

Jeśli chodzi o to, czy można przełknąć wyjątki. To zależy od twojego systemu. Jeśli Twoja aplikacja może obsłużyć przypadki awarii i nie ma korzyści z powiadomienia użytkownika, dlaczego się nie powiodło, to śmiało, chociaż zdecydowanie zalecam, abyś zarejestrował błąd. Zawsze uważałem, że frustrujące jest wezwanie do pomocy w rozwiązaniu problemu i stwierdzenie, że połykali wyjątek (lub zastępowali go i zamiast tego rzucali nowy bez ustawiania wewnętrznego wyjątku).

Generalnie stosuję następujące zasady:

  1. W moich komponentach i bibliotekach łapię wyjątek tylko wtedy, gdy zamierzam go obsłużyć lub zrobić coś na jego podstawie. Lub jeśli chcę podać dodatkowe informacje kontekstowe w wyjątku.
  2. Używam ogólnego try catch w punkcie wejścia do aplikacji lub najwyższego możliwego poziomu. Jeśli pojawi się wyjątek, po prostu go rejestruję i pozwalam mu się nie udać. Idealnie, wyjątki nigdy nie powinny się tutaj pojawiać.

Uważam, że następujący kod to zapach:

try
{
    //do something
}
catch(Exception)
{
   throw;
}

Taki kod nie ma sensu i nie powinien być uwzględniany.

JoshBerke
źródło
@Josh, dobra uwaga na temat połykania wyjątków, ale uważam, że jest kilka przypadków, w których dopuszczalne jest po prostu połykanie wyjątków. W ostatnim projekcie Twój fragment kodu był obowiązkowy, śmierdział. Najgorsze było rejestrowanie ich wszystkich. Moja rada, jeśli nie poradzisz sobie z wyjątkiem, spróbuj go nie połknąć.
smaclell
Zgadzam się, jak powiedziałem, wszystko zależy od aplikacji i konkretnego kontekstu. Są chwile, kiedy połykam wyjątki, chociaż jest to rzadkie i nie pamiętam, kiedy ostatnio to robiłem ;-) To było prawdopodobnie wtedy, gdy pisałem swój własny rejestrator i zapis do dziennika, a drugi dziennik się nie powiódł.
JoshBerke,
Kod służy punktowi: możesz ustawić punkt przerwania w „rzucie”.
Rauhotz
3
Słaby punkt, możesz powiedzieć VS, aby przerywał przy każdym wyrzuconym wyjątku lub możesz zawęzić i wybrać konkretny wyjątek. W VS2008 w obszarze debugowania znajduje się pozycja menu (aby ją znaleźć, musisz dostosować paski narzędzi). Wywołane wyjątki
JoshBerke,
Przykład „zapachu kodu” ma określony efekt uboczny, nawet w tak prostej formie. Jeśli // do somethingzawiera jakiekolwiek try/finallybloki wokół punktu, który rzuca, finallybloki zostaną wykonane przed catchblokiem. Bez try/catchwyjątku poleci na sam szczyt stosu bez wykonywania żadnych finallybloków. Pozwala to programowi obsługi najwyższego poziomu zdecydować, czy wykonać finallybloki, czy nie .
Daniel Earwicker,
9

Chciałbym polecić inne dobre źródło na ten temat. To wywiad z wynalazcami C # i Java, odpowiednio Andersem Hejlsbergiem i Jamesem Goslingiem, na temat Java's Checked Exception.

Awaria i wyjątki

Na dole strony znajdują się również świetne zasoby.

Zgadzam się z Andersem Hejlsbergiem i Tobą, że większości dzwoniących zależy tylko na tym, czy operacja się powiedzie, czy nie.

Bill Venners : Wspomniał Pan o problemach ze skalowalnością i wersjonowaniem w odniesieniu do sprawdzonych wyjątków. Czy mógłbyś wyjaśnić, co masz na myśli przez te dwie kwestie?

Anders Hejlsberg : Zacznijmy od wersjonowania, ponieważ problemy są tam dość łatwe do zauważenia. Powiedzmy, że tworzę metodę foo, która deklaruje, że zgłasza wyjątki A, B i C. W wersji drugiej foo chcę dodać kilka funkcji, a teraz foo może zgłosić wyjątek D. Jest to dla mnie przełomowa zmiana dodaj D do klauzuli throws tej metody, ponieważ istniejący obiekt wywołujący tej metody prawie na pewno nie obsłuży tego wyjątku.

Dodanie nowego wyjątku do klauzuli throws w nowej wersji przerywa kod klienta. To jak dodanie metody do interfejsu. Po opublikowaniu interfejsu jest on niezmienny ze względów praktycznych, ponieważ każda jego implementacja może mieć metody, które chcesz dodać w następnej wersji. Więc zamiast tego musisz utworzyć nowy interfejs. Podobnie z wyjątkami, musiałbyś albo utworzyć zupełnie nową metodę o nazwie foo2, która rzuca więcej wyjątków, albo musiałbyś złapać wyjątek D w nowym foo i przekształcić D w A, B lub C.

Bill Venners : Ale czy w takim razie nie łamiesz ich kodu, nawet w języku bez sprawdzonych wyjątków? Jeśli nowa wersja foo ma rzucić nowy wyjątek, o którym klienci powinni pomyśleć, czy ich kod nie jest zepsuty tylko przez fakt, że nie spodziewali się tego wyjątku podczas pisania kodu?

Anders Hejlsberg : Nie, ponieważ w wielu przypadkach ludzi to nie obchodzi. Nie będą zajmować się żadnym z tych wyjątków. Wokół ich pętli komunikatów znajduje się program obsługi wyjątków dolnego poziomu. Ten przewodnik po prostu wyświetli okno dialogowe z informacją, co poszło nie tak, i będzie kontynuować. Programiści chronią swój kod, pisząc ostatecznie wszędzie try, więc wycofają się poprawnie, jeśli wystąpi wyjątek, ale tak naprawdę nie są zainteresowani obsługą wyjątków.

Klauzula throws, przynajmniej taka, jak jest zaimplementowana w Javie, niekoniecznie zmusza cię do obsługi wyjątków, ale jeśli ich nie obsługujesz, zmusza cię do dokładnego określenia, które wyjątki mogą przejść. Wymaga to albo złapania zadeklarowanych wyjątków, albo umieszczenia ich we własnej klauzuli throws. Aby obejść ten wymóg, ludzie robią śmieszne rzeczy. Na przykład dekorują każdą metodę słowem „rzuca wyjątek”. To po prostu całkowicie pokonuje tę funkcję, a Ty właśnie zmusiłeś programistę do napisania większej ilości papkowatej mazi. To nikomu nie pomaga.

EDYCJA: Dodano więcej szczegółów na temat konwergencji

Gant
źródło
Dzięki za to! Zaktualizowałem moje pytanie o informacje o Twojej odpowiedzi!
AtariPete
Wygląda na to, że pan Hejlsberg usprawiedliwia obsługę wyjątków Pokemona. Jednym z ogromnych problemów z projektowaniem wyjątków w Javie i C # jest to, że zbyt dużo informacji jest zakodowanych w typie wyjątku, podczas gdy informacje powinny być przechowywane w wyjątkowych przypadkach, które nie są dostępne w żaden spójny sposób. Wyjątki powinny rozprzestrzeniać się w górę stosu wywołań, dopóki nie zostaną rozwiązane wszystkie nienormalne warunki, które są przez nie reprezentowane; niestety rodzaj wyjątku - nawet jeśli zostałby rozpoznany - niewiele wskazuje na to, czy sytuacja została rozwiązana. Jeśli wyjątek nie zostanie rozpoznany ...
supercat
... sytuacja jest jeszcze gorsza. Jeśli kod wywołuje FetchDatai zgłasza wyjątek nieoczekiwanego typu, nie ma możliwości sprawdzenia, czy ten wyjątek oznacza po prostu, że dane są niedostępne (w takim przypadku zdolność kodu do obejścia go bez niego „rozwiązałaby” problem) lub czy oznacza to, że procesor się pali i system powinien wykonać „wyłączenie awaryjne” przy pierwszej okazji. Wygląda na to, że pan Hejlsberg sugeruje, że kod powinien zakładać to pierwsze; być może jest to najlepsza możliwa strategia, biorąc pod uwagę istniejące hierarchie wyjątków, ale mimo to wydaje się nieprzyjemna.
supercat
Zgadzam się, że rzucanie Exceptin jest śmieszne, ponieważ prawie wszystko może rzucić wyjątek, wystarczy założyć, że może się to zdarzyć. Ale kiedy określasz wyjątki, takie jak rzuca A, B, C, jest to tylko adnotacja, dla mnie powinien być używany jak blok komentarza. To tak, jakby powiedzieć, hej kliencie mojej funkcji, oto wskazówka, może chcesz zająć się A, B, C, ponieważ te błędy mogą się zdarzyć podczas korzystania ze mnie. Jeśli dodasz D w przyszłości, nie będzie to nic wielkiego, jeśli się tym nie zajmie, ale to tak, jakby dodać nową dokumentację, A tak przy okazji, teraz przydałoby się również złapać D.
Didier A.
8

Zaznaczone wyjątki są ogólnie kontrowersyjną kwestią, aw Javie szczególnie (później postaram się znaleźć kilka przykładów dla tych, którzy są za i przeciw).

Ogólnie rzecz biorąc, obsługa wyjątków powinna obejmować te wytyczne, bez określonej kolejności:

  • Ze względu na łatwość konserwacji, zawsze rejestruj wyjątki, aby gdy zaczniesz widzieć błędy, dziennik pomoże Ci wskazać miejsce, w którym prawdopodobnie zaczął się twój błąd. Nigdy nie odchodź printStackTrace()ani tego nie robisz , istnieje szansa, że ​​któryś z twoich użytkowników w końcu otrzyma jeden z tych śladów stosu i nie będzie miał dokładnie zerowej wiedzy, co z nim zrobić.
  • Łap wyjątki, z którymi możesz sobie poradzić, i tylko te, i obsługuj je , nie wyrzucaj ich po prostu na stos.
  • Zawsze łap konkretną klasę wyjątków i generalnie nigdy nie powinieneś łapać typu Exception, jest bardzo prawdopodobne, że połkniesz inne ważne wyjątki.
  • Nigdy (przenigdy) nie łap Error! , co oznacza: Nigdy nie łapaj Throwables, ponieważ Errors są podklasami tej ostatniej. Errors to problemy, z którymi najprawdopodobniej nigdy nie będziesz w stanie sobie poradzić (np. OutOfMemoryinne problemy z maszyną JVM)

Jeśli chodzi o Twój konkretny przypadek, upewnij się, że każdy klient wywołujący Twoją metodę otrzyma odpowiednią wartość zwracaną. Jeśli coś się nie powiedzie, metoda zwracająca wartość logiczną może zwrócić wartość false, ale upewnij się, że miejsca, które wywołujesz tę metodę, są w stanie to obsłużyć.

Yuval Adam
źródło
Zaznaczone wyjątki nie są problemem w C #, ponieważ ich nie ma.
cletus
3
Imho, czasami dobrze jest wyłapać błędy: Pamiętam bardzo wymagającą pamięć aplikację Java, którą napisałem. Wyłapałem OutOfMemory-Ex i pokazałem użytkownikowi komunikat, że brakuje mu pamięci, że powinien zamknąć inne programy i powiedzieć mu, jak uruchomić jvm z przypisaną większą ilością miejsca na sterty. Myślę, że to pomogło.
Lena Schimmel
5

Powinieneś wychwytywać tylko wyjątki, z którymi możesz sobie poradzić. Na przykład, jeśli masz do czynienia z czytaniem przez sieć, a połączenie przekroczyło limit czasu i pojawi się wyjątek, możesz spróbować ponownie. Jednak jeśli czytasz przez sieć i otrzymujesz wyjątek IndexOutOfBounds, naprawdę nie możesz sobie z tym poradzić, ponieważ nie (cóż, w tym przypadku nie wiesz), co go spowodowało. Jeśli zamierzasz zwrócić false, -1 lub null, upewnij się, że dotyczy to określonych wyjątków. Nie chcę, aby biblioteka, której używam, zwracała wartość false podczas odczytu sieciowego, gdy zgłoszony wyjątek oznacza, że ​​sterta zabraknie pamięci.

Malfist
źródło
3

Wyjątkami są błędy, które nie są częścią normalnego wykonywania programu. W zależności od tego, co robi twój program i jakie są jego zastosowania (np. Edytor tekstu kontra monitor pracy serca), będziesz chciał robić różne rzeczy, gdy napotkasz wyjątek. Pracowałem z kodem, który używa wyjątków jako część normalnego wykonywania i zdecydowanie jest to zapach kodu.

Dawny.

try
{
   sendMessage();

   if(message == success)
   {
       doStuff();
   }
   else if(message == failed)
   {
       throw;
   }
}
catch(Exception)
{
    logAndRecover();
}

Ten kod sprawia, że ​​jestem barfem. IMO nie powinieneś odzyskiwać sprawności po wyjątkach, chyba że jest to krytyczny program. Jeśli rzucasz wyjątki, dzieje się źle.

Holograham
źródło
2

Wszystko to wydaje się rozsądne i często w Twoim miejscu pracy mogą obowiązywać zasady. U nas zdefiniowaliśmy typy wyjątków: SystemException(niezaznaczone) i ApplicationException(zaznaczone).

Uzgodniliśmy, że SystemExceptionjest mało prawdopodobne, aby można je było odzyskać i zostaną rozwiązane raz na szczycie. Aby zapewnić dalszy kontekst, nasi SystemExceptions są exteneded aby wskazać, gdzie wystąpił one np RepositoryException, ServiceEceptionitp

ApplicationExceptionmogą mieć znaczenie biznesowe, takie jak InsufficientFundsExceptionkod klienta i powinny być obsługiwane.

Witam, ale konkretny przykład, trudno jest skomentować twoją implementację, ale nigdy nie użyłbym kodów zwrotnych, są to kwestia konserwacji. Możesz połknąć wyjątek, ale musisz zdecydować, dlaczego i zawsze rejestrować zdarzenie i ślad stosu. Wreszcie, ponieważ twoja metoda nie ma innego przetwarzania, jest dość zbędna (z wyjątkiem hermetyzacji?), Więc doactualStuffOnObject(p_jsonObject);może zwrócić wartość logiczną!


źródło
1

Po zastanowieniu się i przyjrzeniu się kodowi wydaje mi się, że po prostu ponownie zgłaszasz wyjątek jako wartość logiczną. Możesz po prostu pozwolić metodzie przejść przez ten wyjątek (nie musisz go nawet przechwytywać) i zająć się nim w obiekcie wywołującym, ponieważ jest to miejsce, w którym ma to znaczenie. Jeśli wyjątek spowoduje, że obiekt wywołujący ponowi próbę wykonania tej funkcji, obiekt wywołujący powinien przechwytywać wyjątek.

Czasami może się zdarzyć, że napotkany wyjątek nie będzie miał sensu dla dzwoniącego (tj. Jest to wyjątek sieciowy), w takim przypadku należy umieścić go w wyjątku specyficznym dla domeny.

Jeśli z drugiej strony wyjątek sygnalizuje nieodwracalny błąd w twoim programie (tj. Ostatecznym skutkiem tego wyjątku będzie zakończenie programu), osobiście lubię to wyraźnie zaznaczyć, przechwytując go i rzucając wyjątek czasu wykonania.

wds
źródło
1

Jeśli zamierzasz użyć wzorca kodu w swoim przykładzie, wywołaj go TryDoSomething i wyłap tylko określone wyjątki.

Rozważ również użycie filtru wyjątków podczas rejestrowania wyjątków w celach diagnostycznych. VB obsługuje języki filtrów wyjątków. Link do bloga Greggma zawiera implementację, której można używać z poziomu C #. Filtry wyjątków mają lepsze właściwości debugowania niż przechwytywanie i ponowne generowanie. W szczególności można zarejestrować problem w filtrze i pozwolić na dalsze propagowanie wyjątku. Ta metoda umożliwia dołączenie debugera JIT (Just in Time) do pełnego oryginalnego stosu. Ponowny rzut odcina stos w miejscu, w którym został ponownie wyrzucony.

Przypadki, w których TryXXXX ma sens, występują, gdy pakujesz funkcję strony trzeciej, która zgłasza przypadki, które nie są naprawdę wyjątkowe lub są proste do przetestowania bez wywołania funkcji. Przykładem może być coś takiego:

// throws NumberNotHexidecimalException
int ParseHexidecimal(string numberToParse); 

bool TryParseHexidecimal(string numberToParse, out int parsedInt)
{
     try
     {
         parsedInt = ParseHexidecimal(numberToParse);
         return true;
     }
     catch(NumberNotHexidecimalException ex)
     {
         parsedInt = 0;
         return false;
     }
     catch(Exception ex)
     {
         // Implement the error policy for unexpected exceptions:
         // log a callstack, assert if a debugger is attached etc.
         LogRetailAssert(ex);
         // rethrow the exception
         // The downside is that a JIT debugger will have the next
         // line as the place that threw the exception, rather than
         // the original location further down the stack.
         throw;
         // A better practice is to use an exception filter here.
         // see the link to Exception Filter Inject above
         // http://code.msdn.microsoft.com/ExceptionFilterInjct
     }
}

To, czy używasz wzorca takiego jak TryXXX, czy nie, jest bardziej kwestią stylu. Kwestia wyłapywania wszystkich wyjątków i ich połykania nie jest kwestią stylu. Upewnij się, że dozwolone są nieoczekiwane wyjątki!

Steve Steiner
źródło
Uwielbiam wzór TryXXX w .net.
JoshBerke,
1

Proponuję wziąć wskazówki z biblioteki standardowej dla języka, którego używasz. Nie mogę mówić w języku C #, ale spójrzmy na Javę.

Na przykład java.lang.reflect.Array ma setmetodę statyczną :

static void set(Object array, int index, Object value);

Sposób C byłby

static int set(Object array, int index, Object value);

... z wartością zwracaną będącą wskaźnikiem sukcesu. Ale nie jesteś już w świecie C.

Po uwzględnieniu wyjątków powinieneś zauważyć, że sprawia to, że kod jest prostszy i bardziej przejrzysty, przenosząc kod obsługi błędów z dala od podstawowej logiki. Staraj się mieć wiele instrukcji w jednym trybloku.

Jak zauważyli inni - powinieneś być jak najbardziej szczegółowy w rodzaju wychwytywanego wyjątku.

szczupły
źródło
to bardzo ważny komentarz, szanuj język i sposób, w jaki tradycyjnie zarządza on takimi kwestiami. Nie wprowadzaj nastawienia języka C do świata Java.
AtariPete
0

Jeśli zamierzasz złapać wyjątek i zwrócić fałsz, powinien to być bardzo konkretny wyjątek. Nie robisz tego, łapiesz je wszystkie i zwracasz fałsz. Jeśli otrzymam wyjątek MyCarIsOnFireException, chcę się o nim natychmiast dowiedzieć! Reszta wyjątków może mnie nie obchodzić. Powinieneś więc mieć stos modułów obsługi wyjątków, które mówią „whoa whoa, coś tu jest nie tak” dla niektórych wyjątków (wyrzuć ponownie lub złap i wrzuć nowy wyjątek, który lepiej wyjaśnia, co się stało), a dla innych po prostu zwracać fałsz.

Jeśli jest to produkt, który będziesz uruchamiać, powinieneś gdzieś rejestrować te wyjątki, co pomoże ci dostroić rzeczy w przyszłości.

Edycja: Jeśli chodzi o kwestię pakowania wszystkiego w try / catch, myślę, że odpowiedź brzmi tak. Wyjątki powinny być tak rzadkie w twoim kodzie, że kod w bloku catch jest wykonywany tak rzadko, że w ogóle nie wpływa na wydajność. Wyjątkiem powinien być stan, w którym maszyna stanu zepsuła się i nie wie, co robić. Przynajmniej wyrzuć ponownie wyjątek, który wyjaśnia, co się działo w tym czasie i zawiera przechwycony wyjątek. „Wyjątek w metodzie doSomeStuff ()” nie jest zbyt pomocny dla nikogo, kto musi dowiedzieć się, dlaczego się zepsuł, gdy jesteś na wakacjach (lub w nowej pracy).

jcollum
źródło
Nie zapominaj, że konfiguracja bloku wyjątków też kosztuje ...
devstuff,
0

Moja strategia:

Jeśli oryginalna funkcja zwróciła wartość void , zmieniam ją, aby zwracała wartość bool . Jeśli wystąpił wyjątek / błąd, zwróć false , jeśli wszystko było w porządku, zwróć true .

Jeśli funkcja ma coś zwrócić, to w przypadku wystąpienia wyjątku / błędu zwraca wartość null , w przeciwnym razie pozycja zwrotna.

Zamiast bool ciąg może być zwrócony zawierający opis błędu.

W każdym przypadku przed zwróceniem czegokolwiek zarejestruj błąd.

Germstorm
źródło
0

Kilka doskonałych odpowiedzi. Chciałbym dodać, że jeśli skończysz z czymś takim, jak opublikowałeś, przynajmniej wydrukuj więcej niż ślad stosu. Powiedz, co robiłeś w tym czasie, i Ex.getMessage (), aby dać deweloperowi szansę walki.

dj_segfault
źródło
całkowicie się zgadzam. Zrobiłem tylko Ex.printStackTrace (); jako przykład czegoś, co robiłem w holu (tj. nie rzucałem).
AtariPete
0

Bloki try / catch tworzą drugi zestaw logiki osadzony na pierwszym (głównym) zestawie, jako takie są świetnym sposobem na usunięcie nieczytelnego, trudnego do debugowania kodu spaghetti.

Mimo to, używane rozsądnie, zdziałają cuda w czytelności, ale powinieneś po prostu przestrzegać dwóch prostych zasad:

  • używaj ich (oszczędnie) na niskim poziomie, aby przechwytywać problemy związane z obsługą bibliotek i przesyłać je z powrotem do głównego przepływu logicznego. Większość potrzebnej obsługi błędów powinna pochodzić z samego kodu, jako część samych danych. Po co stawiać specjalne warunki, jeśli zwracane dane nie są specjalne?

  • użyj jednej dużej procedury obsługi na wyższym poziomie, aby zarządzać dowolnymi lub wszystkimi dziwnymi warunkami pojawiającymi się w kodzie, które nie są przechwytywane na niskim poziomie. Zrób coś pożytecznego z błędami (dzienniki, restart, odzyskiwanie itp.).

Poza tymi dwoma typami obsługi błędów, cała reszta kodu w środku powinna być wolna i pozbawiona kodu try / catch i obiektów błędów. W ten sposób działa prosto i zgodnie z oczekiwaniami, bez względu na to, gdzie go używasz i co z nim zrobisz.

Paweł.

Paul W. Homer
źródło
0

Może trochę się spóźniam z odpowiedzią, ale obsługa błędów jest czymś, co zawsze możemy zmienić i ewoluować w czasie. Jeśli chcesz poczytać coś więcej na ten temat, napisałem o tym na moim nowym blogu. http://taoofdevelopment.wordpress.com

Miłego kodowania.

GRGodoi
źródło