Czy dobrą praktyką jest złapanie sprawdzonego wyjątku i zgłoszenie wyjątku RuntimeException?

70

Przeczytałem jakiś kod kolegi i stwierdziłem, że często łapie on różne wyjątki, a następnie zawsze zgłasza „RuntimeException”. Zawsze myślałem, że to bardzo zła praktyka. Czy się mylę?

RoflcoptrException
źródło
17
„Cena sprawdzonych wyjątków stanowi naruszenie zasady otwartej / zamkniętej. Jeśli rzucisz sprawdzony wyjątek od metody w kodzie, a połów jest trzy poziomy powyżej, musisz zadeklarować ten wyjątek w podpisie każdej metody między tobą a połowem Oznacza to, że zmiana na niskim poziomie oprogramowania może wymusić zmiany podpisu na wielu wyższych poziomach. ” —Robert C. Martin, «Clean Code», strona 107
Songo,
6
Warto zauważyć, że Jim Waldo sprzeciwia się niesprawdzonym wyjątkom w „Java: The Good Parts” shop.oreilly.com/product/9780596803742.do mówiąc, że dorośli programiści powinni rzucać sprawdzone wyjątki. Przeczytaliśmy to w naszym Dzbanku zaledwie 6 lat temu, kiedy to się pojawiło i wydawało się to dobrą radą! Teraz, przy programowaniu funkcjonalnym, sprawdzone wyjątki są całkowicie nieporęczne. Języki takie jak Scala i Kotlin nawet ich nie mają. Zacząłem również owijać zaznaczone w niezaznaczonych wyjątkach.
GlenPeterson
@GlenPeterson masz również porady w FP, aby całkowicie unikać wykonywania i zamiast tego używać typów sum
jk.
Jest też oczywiste, przypadek interfejsów funkcjonalnej: wbudowane interfejsy funkcjonalne (tj Function, Predicateitp) nie są parametryzowane rzuca klauzul. Oznacza to, że musisz łapać, owijać i ponownie rzucać sprawdzonymi wyjątkami w wewnętrznej pętli dowolnej metody stream (). To samo w sobie podpowiada mi, czy sprawdzone wyjątki są złym pomysłem.
Joel Cornett
Nie ma nic złego w tworzeniu niestandardowych podklas RuntimeException w celu przekazania znaczenia poprzez wyjątek.
Joel Cornett

Odpowiedzi:

55

Nie znam wystarczającego kontekstu, aby wiedzieć, czy twój kolega robi coś niepoprawnie, czy nie, więc zamierzam się o to spierać w sensie ogólnym.

Nie sądzę, aby zmienianie sprawdzonych wyjątków w jakiś smak wyjątku środowiska wykonawczego było zawsze niewłaściwą praktyką . Sprawdzone wyjątkiczęsto nadużywane i nadużywane przez programistów.

Sprawdzone wyjątki są bardzo łatwe w użyciu, gdy nie są przeznaczone do użycia (warunki niemożliwe do odzyskania, a nawet przepływ kontrolny). Zwłaszcza jeśli sprawdzony wyjątek jest używany dla warunków, z których osoba dzwoniąca nie może się odzyskać, myślę, że uzasadnione jest przekształcenie tego wyjątku w wyjątek czasu wykonywania z pomocnym komunikatem / stanem. Niestety, w wielu przypadkach, gdy ktoś staje w obliczu niemożliwego do odzyskania stanu, ma zazwyczaj pustą blokadę, co jest jedną z najgorszych rzeczy, jakie możesz zrobić. Debugowanie takiego problemu jest jednym z największych problemów, jakie może napotkać programista.

Jeśli więc uważasz, że masz do czynienia z sytuacją możliwą do odzyskania, należy ją odpowiednio obsłużyć, a wyjątku nie należy przekształcać w wyjątek czasu wykonywania. Jeśli sprawdzony wyjątek zostanie zastosowany w przypadku niemożliwych do odzyskania warunków, uzasadnione jest przekształcenie go w wyjątek czasu wykonywania .

c_maker
źródło
17
W większości rzeczywistych aplikacji jest bardzo mało niemożliwych do odzyskania warunków. Jest prawie taki poziom, na którym możesz i powinieneś powiedzieć „OK, ta akcja nie powiodła się, więc pokazujemy / logujemy fajny komunikat o błędzie i kontynuujemy / czekaj na następny”.
Michael Borgwardt
6
To prawda, @MichaelBorgwardt, ale miejsce do tego rodzaju obsługi często znajduje się na najwyższym poziomie aplikacji, więc ilekroć widzę, że deweloperzy „obsługują” wyjątki na niższych poziomach, zazwyczaj łatwo jest usunąć ich obsługę i po prostu przesączyć wyjątek w górę. Na przykład środowisko sieciowe, takie jak JSF, przechwytuje wyjątki na najwyższym poziomie, drukuje komunikaty dziennika i kontynuuje przetwarzanie innych żądań (nie mówiąc, że domyślna obsługa jest odpowiednia, tylko przykład).
DavidS,
40

To może być dobre . Proszę przeczytaj:

http://onjava.com/pub/a/onjava/2003/11/19/exceptions.html

W większości przypadków kod klienta nie może nic zrobić z wyjątkami SQLExceptions. Nie wahaj się przekonwertować ich na niesprawdzone wyjątki. Rozważ następujący fragment kodu:

public void dataAccessCode(){
  try{
      ..some code that throws SQLException
  }catch(SQLException ex){
      ex.printStacktrace();
  }
} 

Ten blok catch po prostu pomija wyjątek i nic nie robi. Uzasadnieniem jest to, że mój klient nic nie może zrobić z SQLException. A może poradzisz sobie z tym w następujący sposób?

public void dataAccessCode(){
   try{
       ..some code that throws SQLException
   }catch(SQLException ex){
       throw new RuntimeException(ex);
   }
} 

Konwertuje SQLException na RuntimeException. Jeśli wystąpi wyjątek SQLException, klauzula catch zgłosi nowy wyjątek RuntimeException. Wątek wykonania jest zawieszony, a wyjątek zostaje zgłoszony. Nie niszczę jednak mojej warstwy obiektów biznesowych niepotrzebną obsługą wyjątków, zwłaszcza że nie może ona nic zrobić z wyjątkiem wyjątku SQLException. Jeśli mój catch potrzebuje przyczyny wyjątku głównego, mogę skorzystać z metody getCause () dostępnej we wszystkich klasach wyjątków od JDK1.4.

Rzucanie sprawdzonych wyjątków i niemożność powrotu do zdrowia nie pomaga.

Niektórzy uważają nawet, że sprawdzone wyjątki nie powinny być w ogóle stosowane. Zobacz http://www.ibm.com/developerworks/java/library/j-jtp05254/index.html

Niedawno kilku uznanych ekspertów, w tym Bruce Eckel i Rod Johnson, publicznie oświadczyło, że chociaż początkowo całkowicie zgodzili się z ortodoksyjnym stanowiskiem w sprawie sprawdzonych wyjątków, doszli do wniosku, że wyłączne stosowanie sprawdzonych wyjątków nie jest tak dobrym pomysłem, jak to. pojawiły się na początku, a sprawdzone wyjątki stały się znaczącym źródłem problemów dla wielu dużych projektów. Eckel ma bardziej ekstremalny pogląd, sugerując, że wszystkie wyjątki powinny być odznaczone; Pogląd Johnsona jest bardziej konserwatywny, ale nadal sugeruje, że ortodoksyjna preferencja dla sprawdzonych wyjątków jest nadmierna. (Warto zauważyć, że architekci C #, który prawie na pewno miał duże doświadczenie w korzystaniu z technologii Java, postanowili pominąć sprawdzone wyjątki od projektu języka, czyniąc wszystkie wyjątki niezaznaczonymi wyjątkami.

Również z tego samego linku:

Decyzja o zastosowaniu niesprawdzonych wyjątków jest skomplikowana i jasne jest, że nie ma oczywistej odpowiedzi. Porada Słońca polega na tym, aby używać ich do niczego, podejście C # (z którym zgadzają się Eckel i inni) jest używanie ich do wszystkiego. Inni mówią: „jest środek”.

mrmuggles
źródło
13

Nie, nie mylisz się. Jego praktyka jest bardzo błędna. Powinieneś zgłosić wyjątek wychwytujący problem, który go spowodował. Wyjątek RunTimeException jest szeroki i przekracza zasięg. Powinien to być wyjątek NullPointerException, ArgumentException itp. Cokolwiek dokładnie opisuje to, co poszło nie tak. Zapewnia to możliwość rozróżnienia problemów, które należy rozwiązać i umożliwienia programowi przetrwania, a błędów, które powinny być scenariuszem „Nie przechodź”. To, co robi, jest tylko nieznacznie lepsze niż „Po wznowieniu błędu dalej”, chyba że w informacjach podanych w pytaniu brakuje czegoś.

Takielunek
źródło
1
Dzięki za podpowiedź. A co, jeśli zgłosi niestandardowy wyjątek, który zaimplementował, który dziedziczy bezpośrednio z RuntimeException?
RoflcoptrException
27
@Gary Buyn: wiele osób uważa, że ​​sprawdzony wyjątek to nieudany eksperyment projektowania języka i to one powinny być używane oszczędnie, a nie przyzwyczajenie.
Michael Borgwardt,
7
@Gary Buyn: Oto artykuł, który dość dobrze opisuje debatę: ibm.com/developerworks/java/library/j-jtp05254/index.html Należy również zauważyć, że ponad 15 lat po tym, jak Java wprowadziła tę funkcję, żaden inny język jej nie przyjął, a C ++ wycofało podobną funkcję.
Michael Borgwardt,
7
@c_maker: W rzeczywistości Bloch głównie promuje pogląd ortodoksyjny, a twój komentarz wydaje się dotyczyć głównie korzystania z większej liczby wyjątków i kropek. Uważam, że jedynym uzasadnionym powodem użycia sprawdzonego wyjątku jest warunek, którego oczekuje się od wszystkich abonentów natychmiast.
Michael Borgwardt
14
Niepotrzebne zgłaszanie wyjątków narusza enkapsulację. Co robisz, jeśli prosta metoda, taka jak „getAccounts ()”, rzuca ci „SQLException”, „NullPointerException” lub „FileNotFoundException”? Czy dasz sobie z tym radę? Prawdopodobnie po prostu „złap (wyjątek e) {}”. Poza tym te wyjątki - specyficzne dla jego implementacji! To nie powinno być częścią umowy! Musisz tylko wiedzieć, że wystąpił błąd . A jeśli zmieni się implementacja? Nagle wszystko musi się zmienić, ponieważ metoda nie rzuca już „SQLException”, ale „ParseXMLException”!
KL
8

To zależy.

Ta praktyka może być nawet mądra . Istnieje wiele sytuacji (na przykład przy tworzeniu stron internetowych), w których jeśli zdarzy się jakiś wyjątek, nie będziesz w stanie nic zrobić (ponieważ nie możesz na przykład naprawić niespójnej DB z kodu :-), może to zrobić tylko programista). W takich sytuacjach mądrze jest zawinąć zgłoszony wyjątek w wyjątek środowiska wykonawczego i ponownie go wyrzucić. Następnie możesz wychwycić wszystkie te wyjątki w jakiejś warstwie obsługi wyjątków, zalogować błąd i wyświetlić użytkownikowi miły, zlokalizowany kod błędu + komunikat.

Z drugiej strony , jeśli wyjątkiem nie jest środowisko wykonawcze (jest zaznaczone), twórca interfejsu API wskazuje, że wyjątek ten można rozwiązać i należy go naprawić. Jeśli to możliwe, zdecydowanie powinieneś to zrobić.

Innym rozwiązaniem może być ponowne wrzucenie tego sprawdzonego wyjątku do warstwy wywołującej, ale jeśli nie możesz go rozwiązać, w którym wystąpił wyjątek, prawdopodobnie nie będziesz w stanie rozwiązać go również tutaj ...

malejpavouk
źródło
Masz nadzieję, że twórca API wiedział, co robi i dobrze wykorzystał sprawdzone wyjątki. Zacząłem widzieć interfejsy API, które sprzyjają zgłaszaniu wyjątków środowiska wykonawczego, jednocześnie dokumentując je, aby klient mógł je złapać, jeśli chce.
c_maker
Metoda, która zgłasza wyjątek, zazwyczaj nie wie, czy osoba dzwoniąca może się z niego zregenerować. Z drugiej strony sugerowałbym, że metoda powinna pozwalać na ucieczkę sprawdzonego wyjątku zgłoszonego przez metodę wewnętrzną, jeśli wie, dlaczego metoda wewnętrzna zgłosiła wyjątek, a przyczyna jest zgodna z interfejsem API metody zewnętrznej. Jeśli metoda wewnętrzna nieoczekiwanie zgłasza sprawdzony wyjątek, zezwolenie na jego bąbelkowanie, ponieważ sprawdzony wyjątek może spowodować, że osoba dzwoniąca będzie mylnie poinformowana o tym, co się stało.
supercat
2
Dziękujemy za wspomnienie exception handling layer- np. W aplikacji internetowej, filtrze.
Jake Toronto
5

Chciałbym otrzymywać komentarze na ten temat, ale zdarza mi się, że zdarza się, że niekoniecznie jest to zła praktyka. (Lub okropnie źle). Ale może się mylę.

Często używany interfejs API zgłasza wyjątek, którego nie można sobie wyobrazić w konkretnym przypadku użycia. W takim przypadku wydaje się być w porządku, aby zgłosić wyjątek RuntimeException z wychwyconym wyjątkiem jako przyczyną. Zgłoszenie tego wyjątku prawdopodobnie spowoduje błąd programowy i nie znajdzie się w granicach poprawnej specyfikacji.

Zakładając, że wyjątek RuntimeException nie zostanie później złapany i zignorowany, nie ma go w pobliżu OnErrorResumeNext.

OnErrorResumeNext miałby miejsce, gdy ktoś złapie wyjątek i po prostu go zignoruje lub po prostu wydrukuje. Jest to strasznie zła praktyka w prawie wszystkich przypadkach.

użytkownik606723
źródło
Może się tak zdarzyć w górnej części drzewa połączeń, gdzie jedyną rzeczą, którą możesz zrobić, jest próba odzyskania wdzięku, a znajomość konkretnego błędu naprawdę nie pomoże. W takim przypadku może być konieczne zarejestrowanie błędu i przejście do następnego kroku (przetworzenie następnego rekordu, poinformowanie użytkownika o wystąpieniu błędu itp.). W przeciwnym razie nie. Powinieneś zawsze traktować wyjątki tak blisko błędu, jak to praktyczne, a nie owijać je jako białego słonia dla następnego przewodnika.
Michael K,
@MichaelK Problem jest „tak bliski, jak to praktyczne”, w praktyce często oznacza „przez kilka pośrednich warstw, które są poza twoją bezpośrednią kontrolą”. Na przykład, jeśli moja klasa musi zaimplementować określony interfejs, mam związane ręce. Może się to zdarzyć dowolnie głęboko w drzewie połączeń. Nawet gdy interfejsy są pod moją kontrolą, dodawanie deklaracji rzutów może sprawić, że abstrakcja stanie się nieszczelna, jeśli istnieje tylko ograniczony zestaw konkretnych implementacji, które można sobie wyobrazić. Nakładanie przez każdego klienta kosztu na szczegóły wdrożenia kilku nie jest świetnym kompromisem projektowym IMO.
Tim Seguine
4

TL; DR

Przesłanka

  • Wyjątki czasu wykonywania powinny być zgłaszane, gdy błąd jest nieusuwalny: gdy błąd znajduje się w kodzie i nie zależy od stanu zewnętrznego (dlatego odzyskiwanie poprawiałoby kod).
  • Zaznaczone wyjątki należy zgłaszać, gdy kod jest poprawny, ale stan zewnętrzny nie jest zgodny z oczekiwaniami: brak połączenia sieciowego, nie znaleziono pliku lub jest on uszkodzony itp.

Wniosek

Możemy ponownie sprawdzić sprawdzony wyjątek jako wyjątek środowiska wykonawczego, jeśli kod propagacyjny lub kod interfejsu zakłada, że ​​podstawowa implementacja zależy od stanu zewnętrznego, gdy jest to oczywiste.


W tej sekcji omówiono, kiedy należy zgłosić jeden z wyjątków. Możesz przejść do następnego poziomego paska, jeśli chcesz przeczytać bardziej szczegółowe wyjaśnienie wniosku.

Kiedy należy zgłosić wyjątek czasu wykonywania? Zgłaszasz wyjątek środowiska wykonawczego, gdy jest jasne, że kod jest niepoprawny, a odzyskiwanie jest odpowiednie poprzez modyfikację kodu.

Na przykład należy zgłosić wyjątek czasu wykonywania dla następujących elementów:

float nan = 1/0;

Spowoduje to wygenerowanie podziału przez zero wyjątku czasu wykonywania. Jest to właściwe, ponieważ kod jest uszkodzony.

Lub na przykład, oto część HashMapkonstruktora:

public HashMap(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: " +
                loadFactor);
    // more irrelevant code...
}

Aby naprawić początkową pojemność lub współczynnik obciążenia, należy edytować kod, aby upewnić się, że przekazywane są prawidłowe wartości. Nie zależy to od działania jakiegoś odległego serwera, od aktualnego stanu dysku, plik lub inny program. Wywołanie konstruktora z niepoprawnymi argumentami zależy od poprawności kodu wywołującego, czy to z powodu niewłaściwego obliczenia, które doprowadziło do niepoprawnych parametrów lub niewłaściwego przepływu, który przeoczył błąd.

Kiedy należy zgłosić sprawdzony wyjątek? Zgłaszasz wyjątek, gdy problem można naprawić bez zmiany kodu. Lub, mówiąc inaczej, rzucasz sprawdzony wyjątek, gdy błąd jest związany ze stanem, gdy kod jest poprawny.

Teraz słowo „odzyskać” może być trudne. Może to oznaczać, że znajdziesz inny sposób na osiągnięcie celu: Na przykład, jeśli serwer nie odpowiada, powinieneś wypróbować następny serwer. Jeśli tego rodzaju odzyskiwanie jest możliwe w twoim przypadku, to świetnie, ale to nie jedyna rzecz, którą oznacza odzyskiwanie - odzyskiwanie może po prostu wyświetlać użytkownikowi okno dialogowe błędu wyjaśniające, co się stało, lub jeśli jest to aplikacja serwerowa, może to być wysyłanie wiadomości e-mail do administratora, a nawet zwykłe rejestrowanie błędu w odpowiedni i zwięzły sposób.

Weźmy przykład wspomniany w odpowiedzi mrmugglesa:

public void dataAccessCode(){
   try{
       ..some code that throws SQLException
   }catch(SQLException ex){
       throw new RuntimeException(ex);
   }
}

To nie jest poprawny sposób obsługi sprawdzonego wyjątku. Sama niezdolność do obsługi wyjątku w zakresie tej metody nie oznacza, że ​​aplikacja powinna ulec awarii. Zamiast tego należy propagować go do wyższego zakresu, takiego jak:

public Data dataAccessCode() throws SQLException {
    // some code that communicates with the database
}

Co pozwala na odzyskanie przez dzwoniącego:

public void loadDataAndShowUi() {
    try {
        Data data = dataAccessCode();
        showUiForData(data);
    } catch(SQLException e) {
        // Recover by showing an error alert dialog
        showCantLoadDataErrorDialog();
    }
}

Sprawdzone wyjątki są narzędziem analizy statycznej, wyjaśniają programiście, co może pójść nie tak w określonym wywołaniu bez konieczności uczenia się implementacji lub przechodzenia przez proces prób i błędów. Ułatwia to upewnienie się, że żadna część przepływu błędów nie zostanie zignorowana. Ponowne sprawdzenie sprawdzonego wyjątku jako wyjątku środowiska wykonawczego działa przeciwko tej oszczędzającej pracę funkcji analizy statycznej.

Warto również wspomnieć, że warstwa wywołująca ma lepszy kontekst większego schematu rzeczy, jak pokazano powyżej. Może być wywoływanych wiele przyczyn dataAccessCode, konkretny powód połączenia jest widoczny tylko dla dzwoniącego - w ten sposób jest w stanie podjąć lepszą decyzję w sprawie prawidłowego odzyskania po awarii.

Teraz, gdy udało nam się wyczyścić to rozróżnienie, możemy przystąpić do dedukcji, gdy będzie w stanie ponownie sprawdzić sprawdzony wyjątek jako wyjątek czasu wykonywania.


Biorąc pod uwagę powyższe, kiedy właściwe jest ponowne zwrócenie sprawdzonego wyjątku jako RuntimeException? Gdy używany kod zakłada zależność od stanu zewnętrznego, ale można wyraźnie stwierdzić, że nie zależy on od stanu zewnętrznego.

Rozważ następujące:

StringReader sr = new StringReader("{\"test\":\"test\"}");
try {
    doesSomethingWithReader(sr); // calls #read, so propagates IOException
} catch (IOException e) {
    throw new IllegalStateException(e);
}

W tym przykładzie kod się propaguje, IOExceptionponieważ interfejs API Readerjest przeznaczony do uzyskiwania dostępu do stanu zewnętrznego, jednak wiemy, że StringReaderimplementacja nie ma dostępu do stanu zewnętrznego. W tym zakresie, w którym możemy z pewnością stwierdzić, że części biorące udział w połączeniu nie mają dostępu do IO ani żadnego innego stanu zewnętrznego, możemy bezpiecznie przywrócić wyjątek jako wyjątek czasu wykonywania bez zadziwiających kolegów, którzy nie są świadomi naszej implementacji (i być może zakładając, że kod dostępu do IO wyrzuci an IOException).


Powodem ścisłego sprawdzania wyjątków zależnych od stanu zewnętrznego jest to, że nie są one deterministyczne (w przeciwieństwie do wyjątków zależnych od logiki, które będą przewidywalnie odtwarzane za każdym razem dla wersji kodu). Na przykład, jeśli spróbujesz podzielić przez 0, zawsze wygenerujesz wyjątek. Jeśli nie podzielisz przez 0, nigdy nie wygenerujesz wyjątku i nie będziesz musiał obsługiwać tego wyjątku, ponieważ nigdy się nie zdarzy. W przypadku uzyskania dostępu do pliku, sukces raz nie oznacza, że ​​odniesiesz sukces następnym razem - użytkownik mógł zmienić uprawnienia, inny proces mógł go usunąć lub zmodyfikować. Więc zawsze musisz poradzić sobie z tym wyjątkowym przypadkiem, w przeciwnym razie możesz mieć błąd.

Ben Barkay
źródło
2

Do samodzielnych aplikacji. Jeśli wiesz, że Twoja aplikacja nie może obsłużyć wyjątku, możesz zamiast rzucać zaznaczony RuntimeException, rzucić błąd, pozwolić aplikacji na awarię, mieć nadzieję na raporty o błędach i naprawić aplikację. (Zobacz odpowiedź mrmuggles, aby uzyskać bardziej szczegółową dyskusję na temat zalet i wad sprawdzonych kontra niezaznaczonych).

Kasper van den Berg
źródło
2

Jest to powszechna praktyka w wielu ramach. Np. HibernateRobi dokładnie to. Chodzi o to, że interfejsy API nie powinny być uciążliwe dla klienta i Exceptionsą uciążliwe, ponieważ musisz jawnie napisać kod, aby obsłużyć je w miejscu, w którym wywołujesz interfejs API. Ale to miejsce może nie być właściwym miejscem do obsługi wyjątku.
Właściwie to szczerze mówiąc jest to „gorący” temat i wiele sporów, więc nie będę się sprzeciwiał, ale powiem, że to, co robi / proponuje twój przyjaciel, nie jest niczym niezwykłym ani niezwykłym.

użytkownik10326
źródło
1

Cały ten „sprawdzony wyjątek” to zły pomysł.

Programowanie strukturalne pozwala na przekazywanie informacji między funkcjami (lub, w języku Java, metodami) tylko wtedy, gdy są one „w pobliżu”. Dokładniej, informacje można przenosić między funkcjami tylko na dwa sposoby:

  1. Od dzwoniącego do rozmówcy poprzez przekazywanie argumentów.

  2. Od odbiorcy do osoby dzwoniącej, jako wartości zwracane.

To jest zasadniczo dobra rzecz. Oto, co pozwala ci lokalnie uzasadnić swój kod: jeśli potrzebujesz zrozumieć lub zmodyfikować część programu, wystarczy spojrzeć na tę część i inne „pobliskie”.

Jednak w niektórych okolicznościach konieczne jest przesłanie informacji do „odległej” funkcji, bez wiedzy nikogo w środku. To właśnie wtedy trzeba zastosować wyjątki. Wyjątkiem jest tajna wiadomość wysyłana z modułu wywołującego (dowolna część kodu może zawierać throwinstrukcję) do modułu obsługi (dowolna część kodu może zawierać catchblok zgodny z wyjątkiem, który był thrown).

Sprawdzone wyjątki niszczą tajemnicę mechanizmu, a wraz z nim samą przyczynę jego istnienia. Jeśli funkcja może pozwolić na to, by jej rozmówca „znał” pewną informację, wystarczy wysłać tę informację bezpośrednio jako część wartości zwracanej.

pyon
źródło
Warto wspomnieć, że tego rodzaju problem może naprawdę siać spustoszenie w przypadkach, gdy metoda uruchamia funkcję dostarczaną przez jej program wywołujący. Autor metody otrzymującej funkcję w wielu przypadkach nie będzie miał powodu wiedzieć ani przejmować się tym, czego oczekuje osoba wywołująca, ani jakich wyjątków może się spodziewać. Jeśli kod odbierający metodę nie spodziewa się, że wygeneruje sprawdzony wyjątek, dostarczona metoda może być zmuszona do zawinięcia wszystkich sprawdzonych wyjątków, które wprowadziłaby w niezaznaczone wyjątki, które następnie dostawca mógł wychwycić.
supercat
0

Może to zależeć od przypadku. W niektórych scenariuszach mądrze jest robić to, co robi twój przyjaciel, na przykład, gdy udostępniasz interfejs API niektórym klientom i chcesz, aby klient był najmniej świadomy szczegółów implementacji, gdzie wiesz, że pewne wyjątki implementacji mogą być specyficzne dla szczegóły implementacji i niewymienialne dla klienta.

Trzymając z dala sprawdzone wyjątki, możesz ujawnić interfejsy API, które umożliwiłyby klientowi napisanie czystszego kodu, ponieważ sam klient może wstępnie sprawdzać poprawność wyjątkowych warunków.

Na przykład Integer.parseInt (String) pobiera ciąg znaków i zwraca jego całkowity odpowiednik i zgłasza NumberFormatException w przypadku, gdy ciąg nie jest liczbą. Teraz wyobraź sobie, że przesłanie formularza z polem agejest konwertowane za pomocą tej metody, ale klient już zapewniłby weryfikację z jego strony, więc nie ma sensu wymuszać sprawdzania wyjątku.

użytkownik94369
źródło
0

Naprawdę jest tutaj kilka pytań

  1. Czy przekształcić sprawdzone wyjątki w niesprawdzone?

Ogólna zasada jest taka, że ​​należy sprawdzić wyjątki, które osoba dzwoniąca może wychwycić i odzyskać. Inne wyjątki (te, w których jedynym rozsądnym rezultatem jest przerwanie całej operacji lub gdy uważasz, że są one na tyle mało prawdopodobne, że martwienie się o ich obsługę nie jest tego warte) powinny zostać odznaczone.

Czasami twoja ocena, czy wyjątek zasługuje na złapanie i odzyskanie, różni się od oceny interfejsu API, z którym pracujesz. Czasami kontekst ma znaczenie, wyjątek, który jest wart uwagi w jednej sytuacji, może nie być wart uwagi w innej. Czasami twoja ręka jest wymuszona przez istniejące interfejsy. Tak więc istnieją uzasadnione powody, aby zamienić sprawdzony wyjątek w niesprawdzony wyjątek (lub na inny typ sprawdzonego wyjątku)

  1. Jeśli zamierzasz zamienić niesprawdzony wyjątek w sprawdzony wyjątek, jak to zrobić.

Po pierwsze i najważniejsze upewnij się, że korzystasz z mechanizmu łączenia wyjątków. W ten sposób informacje z oryginalnego wyjątku nie zostaną utracone i można je wykorzystać do debugowania.

Po drugie, musisz zdecydować, jakiego typu wyjątku użyć. Użycie zwykłego wyjątku czasu wykonywania utrudnia dzwoniącemu ustalenie, co poszło nie tak, ale jeśli dzwoniący próbuje ustalić, co poszło źle, może to oznaczać, że nie należy zmieniać wyjątku, aby usunąć zaznaczenie.

Peter Green
źródło
0

W pytaniu boolowskim trudno jest odpowiedzieć inaczej po dwóch kontrowersyjnych odpowiedziach, ale chciałbym dać wam perspektywę, o której nawet wspomniałem w kilku miejscach, nie było wystarczająco podkreślone ze względu na jej znaczenie.

Z biegiem lat odkryłem, że zawsze ktoś jest zdezorientowany, jeśli chodzi o trywialny problem, brakuje mu zrozumienia niektórych podstaw.

Nakładanie warstw. Aplikacja (przynajmniej powinna być) stos warstw jedna na drugiej. Jednym z ważnych oczekiwań dotyczących dobrego nakładania warstw jest to, że niższe warstwy zapewniają funkcjonalność dla potencjalnie wielu składników z górnej warstwy.

Załóżmy, że twoja aplikacja ma następujące warstwy od podstaw: NET, TCP, HTTP, REST, MODEL DANYCH, BIZNES.

Jeśli twoja warstwa biznesowa chce wykonać połączenie reszty ... poczekaj sekundę. Dlaczego to powiedziałem? Dlaczego nie powiedziałem żądania HTTP lub transakcji TCP ani nie wysłałem pakietów sieciowych? Ponieważ nie mają one znaczenia dla mojej warstwy biznesowej. Nie zamierzam się nimi zajmować, nie będę zaglądać w ich szczegóły. Jestem całkowicie w porządku, jeśli są głęboko w wyjątkach, które dostaję jako przyczynę i nie chcę wiedzieć, że w ogóle istnieją.

Co więcej, źle jest, jeśli znam szczegóły, ponieważ jeśli jutro chciałbym zmienić podkreślające protokoły transportowe dotyczące szczegółów specyficznych dla protokołu TCP, oznacza to, że moja abstrakcja REST nie wykonała dobrej pracy, aby się z niej wyodrębnić konkretne wdrożenie.

Przy przenoszeniu wyjątku z warstwy na warstwę ważne jest, aby ponownie zapoznać się z każdym jego aspektem i jaki sens ma to dla abstrakcji, jaką zapewnia bieżąca warstwa. Może być zastąpienie wyjątku innym, może łączyć kilka wyjątków. Może również zmienić je z zaznaczonego na niezaznaczone lub odwrotnie.

Oczywiście, faktyczne miejsca, o których wspomniałeś, mają sens, to inna historia, ale ogólnie - tak, to może być dobra rzecz do zrobienia.

gsf
źródło
-2

W mojej opinii,

Na poziomie ramowym powinniśmy być wyjątkami środowiska wykonawczego catch, aby zredukować więcej bloków try catch dla wywołującego w tym samym miejscu.

Na poziomie aplikacji rzadko wychwytujemy wyjątki czasu wykonywania i myślę, że ta praktyka była zła.

Mark xie
źródło
1
A co zrobisz z tymi wyjątkami na poziomie ramowym?
Matthieu,
Jeśli w strukturze znajduje się warstwa interfejsu użytkownika, która może obsłużyć wyjątki, interfejs użytkownika wyświetli komunikat o błędzie, że coś poszło nie tak. W przypadku jednostronicowej aplikacji javascript aplikacja może wyświetlać komunikat o błędzie. To prawda, że ​​warstwa interfejsu użytkownika powinna obsłużyć błąd tylko wtedy, gdy głębsza warstwa naprawdę nie może naprawić błędu.
Jake Toronto