Czy nie jest dobrą praktyką zajmowanie się wyjątkami środowiska wykonawczego w kodzie?

11

Pracuję nad aplikacją Java i widzę, że wyjątki czasu wykonywania są obsługiwane w wielu miejscach. Na przykład,

try {
    // do something
} catch(NullPointerException e) {
    return null;
}

Moje pytania brzmią: kiedy dobrą praktyką jest zajmowanie się wyjątkami Runtime? Kiedy wyjątki powinny pozostać nieobsługiwane?

Vinoth Kumar CM
źródło
7
-1: Nie ma jednej odpowiedzi na tak szerokie i niejasne pytanie. Niemożliwe jest podanie jednej odpowiedzi na pytanie tak nieostre jak to. To pytanie jest okropne. Podaj konkretne przykłady wątpliwości.
S.Lott,
@ S.Lott Nie zgadzam się w tym przypadku, ponieważ wydaje się, że jest pewien podzbiór programistów, którzy mają w głowie, że tak jest w przypadku braku „dobrej” racjonalności.
SoylentGray
2
@Chad: „tak jest bez„ dobrej ”racjonalności. To może być prawda. Ale jedyną możliwą odpowiedzią musi być „to zależy”. Dlatego pytanie wydaje się błędne.
S.Lott,

Odpowiedzi:

18

To zależy.

Na przykład Integer#parseIntwyrzuca NumberFormatException(który jest RTE), jeśli podany ciąg nie może zostać przeanalizowany. Ale na pewno nie chcesz, aby aplikacja uległa awarii tylko dlatego, że użytkownik napisał „x” w polu tekstowym, które było liczbą całkowitą? A skąd wiesz, czy ciąg może zostać przeanalizowany, chyba że spróbujesz go najpierw przeanalizować? Tak więc w tym przypadku RTE jest tylko sygnałem błędu, który powinien spowodować jakiś komunikat błędu. Można argumentować, że powinien to być sprawdzony wyjątek, ale co możesz zrobić - tak nie jest.

Joonas Pulakka
źródło
2
Zgadzam się z „To zależy”, ale twój przykład parsowania wydaje się raczej złym projektem API. Integer#parseIntpowinien naprawdę zwrócić Maybe<Integer>zamiast tego i nie zgłaszać żadnych wyjątków.
Jörg W Mittag,
3
@ Jörg W Mittag: Zgadzam się, że to zły projekt API, ale to prawdziwy świat, z którym mamy do czynienia. Jak właściwie obsługiwać wartości rzucane / zwracane zależy od tego, jak naprawdę działa świat , a nie od tego, jak powinien on optymalnie działać :-)
Joonas Pulakka
Chodzi o to, że możesz zrobić coś znaczącego dla przewidywanego warunku (dane wejściowe użytkownika nie są liczbami całkowitymi). Samo połknięcie NPE jest złym stylem i po prostu zatuszuje istniejące błędy programistyczne.
Jürgen Strobel,
7

Wyjątki NullPointerExceptions są zwykle oznaką braku kontroli null. Zamiast więc łapać go w ten sposób, należy dodać odpowiedni test zerowy, aby upewnić się, że nie zostanie zgłoszony wyjątek.

Ale czasem właściwe jest obsługiwanie wyjątków RunTimeExcept. Na przykład, gdy nie można zmodyfikować kodu, aby dodać kontrolę zerową w odpowiednim miejscu, lub gdy wyjątkiem jest coś innego niż wyjątek NullPointerException.

Twój przykład postępowania z wyjątkami jest okropny. W ten sposób tracisz ślad stosu i dokładne informacje o problemie. I tak naprawdę nie rozwiązujesz tego, ponieważ prawdopodobnie wywołasz inny wyjątek NullPointerException w innym miejscu i uzyskasz mylące informacje o tym, co się wydarzyło i jak to rozwiązać.

deadalnix
źródło
1
Chciałbym dodać, że kontrola zerowa jest lepszą decyzją pod względem wydajności.
Mahmoud Hossam,
... mimo to, jeśli dokonujesz dziesiątek alokacji, które „nigdy nie powinny zawieść” w jednym miejscu, dodanie zerowych kontroli dla każdego z nich może śmiesznie zaciemnić kod. return null;Lepszym rozwiązaniem będzie wyjątek typu catch-all (który BARDZO poradzi sobie z sytuacją z wdziękiem, nie tylko ).
SF.
Prawdopodobnie mylisz java z czymś innym (C ++ na przykład). Gdy alokacja nie powiedzie się, pojawia się błąd OutOfMemoryError lub coś podobnego, nigdy wskaźnik zerowy. Ukrywanie znaku zerowego zamiast wyjątku ukrywa błąd oczekiwania na wybuch kodu w innym miejscu.
deadalnix
newrzuca std::bad_allocw C ++.
R. Martinho Fernandes,
Zakładając, że nowy operator nie jest przeciążony, co jest powszechną praktyką. W C ++ wszystko może się zdarzyć;) Ale OK, powiedzmy C malloc. Ważne było to, że nigdy nie dostałeś tego w java.
deadalnix
4

Obsługuję Oczekiwane wyjątki tam, gdzie ich oczekuję. (Jak błędy odczytu / zapisu DB). Nieoczekiwane wyjątki. Gdzie indziej może oczekiwać wyjątku i mieć na to logikę.

SoylentGray
źródło
2

Wyjątki powinny być właśnie… wyjątkami. Najlepszą praktyką przy stosowaniu wyjątków jest wykorzystanie ich do uwzględnienia sytuacji, w której dzieje się coś sprzecznego z tym, czego się spodziewałbyś. Klasycznym przykładem jest FileNotFoundException, który jest generowany, gdy pliku po prostu nie ma. Jeśli testujesz istnienie pliku, użyj File.exists (), ponieważ po prostu naciskasz 10-metrowym drążkiem, aby sprawdzić, czy coś nie trafi.

Można technicznie osiągnąć te same wyniki, otaczając go w próbach catch i używając pliku tak, jakby istniał, ale A) wyjątki są generalnie kosztowne pod względem zasobów i B) programiści zakładają, że masz na myśli istnienie pliku, gdyby był w try catch, co zwiększa ogólne zamieszanie w programie.

Jest wiele sytuacji, w których napiszę metodę, która pobiera pewną wartość z bazy danych. Tysiąc rzeczy może się nie udać, a ponieważ potrzebuję tylko jednej małej informacji, niewygodne jest otaczanie połączenia listą próbną zawierającą 5 różnych wyjątków. Więc złapię wyjątki w metodzie pobierania. Jeśli coś pójdzie nie tak, podejmę wszelkie odpowiednie działania, aby zamknąć połączenie z bazą danych lub cokolwiek innego w klauzuli ostatecznie i zwrócę null. Jest to dobra praktyka nie tylko dlatego, że upraszcza twój kod, ale również dlatego, że „null” wysyła tę samą wiadomość, którą mógłbyś otrzymać z wyjątku .. że coś nie poszło zgodnie z planem. Zarządzaj specyfikacjami wyjątków w metodzie pobierania, ale zarządzaj, co robić, gdy rzeczy nie są

Na przykład:

Integer getUserCount() {
   Integer result = null;
   try {
      // Attempt to open database and retrieve data
   } catch (TimeoutException e) {
      logger.error("Got a watch?");
   } catch (MissingDatabaseException e) {
      logger.error("What are you smoking?");
   } catch (PermissionsToReadException e) {
      logger.error("Did you *really* think you were getting away with that?");
   } catch (PressedSendButtonToHardException e) {
      logger.error("Seriously.. just back away from the computer... slowly..");
   } catch (WTFException e) {
      logger.error("You're on your own with this one.. I don't even know what happened..");
   } finally {
      // Close connections and whatnot
   }
   return result;
}

void doStuff() {
   Integer result = getUserCount();
   if(result != null) {
       // Went as planned..
   }
}
Neil
źródło
6
Jeśli użyjesz File.exists () i polegasz na wyniku, sytuacja wyścigu może się zdarzyć, jeśli plik zostanie usunięty między File.exists () a File.open (). Jeśli spowoduje to błąd krytyczny dla bezpieczeństwa, osoba atakująca może celowo spowodować ten warunek. Z tego powodu czasem lepiej zachować atomowość, tzn. Wypróbować i złapać wyjątek.
user281377,
1
Istnieją również pliki, które muszą istnieć do działania aplikacji. To byłyby wyjątkowe warunki. Jeśli istnieje plik, który musisz mieć, aby aplikacja działała, nie ma powodu, aby z niego nie czytać, a następnie obsługiwać wyjątek w wyjątkowym przypadku. Wierzę, że to wyjaśnia cel.
Thomas Owens
To zła decyzja o zwróceniu wartości null. W pewnym momencie spowoduje to wyjątek NullPointerException i będzie bardzo trudno debugować to, co poszło źle. Ponieważ ktoś w pewnym momencie zapomni o zerowym czeku.
deadalnix
@deadalnix: Mógłbym argumentować, że równie łatwo można zapomnieć o próbowaniu w inny sposób. Różnica jest kwestią stylu, a nie funkcjonalności.
Neil,
@ammoQ: Nie zgadzam się. Myślę, że powinieneś użyć File.exists () i w rzadkich okolicznościach, że zostanie on usunięty przed użyciem, wyjątek jest więcej niż odpowiedni. Różnica polega na tym, gdzie trzymasz swój haczyk. Po prostu miej łapacz wyjątków dla nieprzewidzianych błędów, aby móc się zalogować i zgłosić.
Neil,
-5

tak, poprawna obsługa wyjątków czasu wykonywania nie jest dobrą praktyką. Powodem jest to, że uważa się, że jest to kosztowne / wymaga dużej ilości pamięci.

prasonscala
źródło
5
Cześć prassee, czy możesz rozwinąć swoją odpowiedź? One-linery, które nie są poparte faktami, referencjami lub doświadczeniami, nie są zbyt pomocne.