Wyjątki dotyczące „błędu programowania” - Czy moje podejście jest prawidłowe?

9

Obecnie próbuję ulepszyć sposób korzystania z wyjątków i znalazłem ważne rozróżnienie między wyjątkami, które oznaczają błędy programowania (np. Ktoś przekazał wartość NULL jako argument lub wywołał metodę na obiekcie po jego usunięciu), a tymi, które oznaczają błąd w operacja, która nie jest błędem osoby dzwoniącej (np. wyjątek We / Wy).

Jak te dwa wyjątki należy traktować inaczej? Czy uważasz, że wyjątki od błędów muszą być wyraźnie udokumentowane, czy wystarczy, aby udokumentować odpowiednie warunki wstępne? Czy możesz pominąć dokumentację warunku wstępnego lub wyjątku dotyczącego błędu, jeśli powinno to być oczywiste (na przykład ObjectDisposedExceptionpodczas wywoływania metody na usuwanym obiekcie)

Medo42
źródło
Zazwyczaj wyróżniam inną kategorię wyjątków: wyjątki biznesowe. Odnoszą się one do warunków błędu, których użytkownik jest zainteresowany szczegółami. Zazwyczaj sprawdzam te wyjątki.
beluchin

Odpowiedzi:

2

Myślę, że jesteś na dobrej drodze. Ani rzucanie, łapanie, ani dokumentowanie wszystkich potencjalnie możliwych do rzucenia wyjątków nie ma większego sensu. Są chwile, w których rygorystyczność produktu wymaga wyższego stopnia wyjątkowego zatrudnienia i dokumentacji (np. Pewne aspekty krytyczne dla bezpieczeństwa systemów).

Strategia polegająca na bardziej defensywnym wykorzystaniu koncepcji kontraktowych do identyfikowania warunków wstępnych (i dodatkowych) na szczególnie dzwoniących w dalszej kolejności (np. Wszystko, co przypomina członka publicznego lub chronionego) będzie często bardziej skuteczna i elastyczna. Dotyczy to nie tylko wdrożenia, ale także dokumentacji. Jeśli programiści wiedzą, czego się spodziewają, są bardziej skłonni do przestrzegania reguł i rzadziej mogą się pomylić lub niewłaściwie wykorzystać napisany kod.

Niektóre z typowych rzeczy, które powinny być udokumentowane, obejmują przypadek parametrów zerowych. Często wiąże się to z konsekwencją ich zastosowania, która prowadzi do czegoś, czego normalnie nie można się spodziewać, ale jest dozwolona i używana z różnych powodów, czasem ze względu na elastyczność. Jako konsument członka, który ma parametry, które pozwalają na wartość zerową lub inne specjalne, nieracjonalne wartości (takie jak czas ujemny lub wartości ujemne), spodziewam się, że zostaną zidentyfikowane i wyjaśnione.

W przypadku parametrów innych niż null, jako konsument członka publicznego lub chronionego, chcę wiedzieć, że wartość null jest niedozwolona. Chcę wiedzieć, jaki jest prawidłowy zakres wartości w danym kontekście. Chcę poznać konsekwencje używania wartości, które są poza normalnym zakresem, ale poza tym są poprawne w innym kontekście wywoływania (np. Wartość typu jest ogólnie poprawna dla dowolnej operacji, ale nie tutaj - jak parametr boolowski, który nie nie należy oczekiwać wartości false jako prawidłowej wartości.

Jeśli chodzi o platformę lub inne dobrze znane interfejsy, nie sądzę, abyś musiał się starać w dokumentowaniu. Ponieważ jednak jako programista masz możliwość różnicowania implementacji w zależności od wskazówek platformy, należy pamiętać o tym, w jaki sposób wskazówki te mogą być wartościowe.

Specyficzne dla IDisposable, często implementacje tego interfejsu oferują alternatywną metodę, która jest lepsza niż proces jawnego usuwania. W takich przypadkach zaznacz preferowaną metodę i zauważ, że wyraźne usuwanie nie jest preferowane.

JustinC
źródło
Rozumiem potrzebę dokumentowania, które wartości są dozwolone, ale jeśli zobaczysz funkcję performReadCommand(ICommand cmd, int replySizeBytes)- czy spodziewałbyś się, że wartość null byłaby akceptowalna dla cmd lub wartość ujemna dla replySizeBytes? Dokumentacja IMO byłaby konieczna tylko wtedy, gdyby te wartości były rzeczywiście dozwolone, i prawdopodobnie powinieneś się zastanowić, czy 0 jest ważne dla replySizeBytes.
Medo42,
Oba mogą być „ważne” w zależności od implementacji. Ty lub ja, i nawet wszyscy tutaj nie oczekują, że wartości zerowe lub ujemne będą odpowiednie dla tego podpisu, ale mogą być. Rozważ środowisko, w którym czytnik jest blokującym, trwałym procesem, a gdy cmd ma wartość NULL, może to oznaczać, że nie zmienisz polecenia używanego przez czytnik, i przejdziesz do następnego odczytu przy użyciu poprzedniego polecenia. W takim przypadku najlepiej byłoby zastosować bardziej odpowiednią sygnaturę metody, która zwolniła cmd jako parametr, ale może nie być.
JustinC,
2

Oto moje własne myśli. Zauważ, że nie jestem zbyt pewien, że jest to najlepsza droga, dlatego właśnie to pytanie stworzyłem.

O ile rozumiem, nie ma sensu, aby natychmiastowy rozmówca obsługiwał wyjątki dotyczące błędów programistycznych, powinien zamiast tego zapewnić spełnienie warunków wstępnych. Tylko „zewnętrzne” procedury obsługi wyjątków na granicach zadań powinny je wychwycić, aby mogły utrzymać działanie systemu w przypadku niepowodzenia zadania.

Aby mieć pewność, że kod klienta będzie w stanie przechwycić wyjątki „awarii” bez przypadkowego wychwycenia wyjątków błędów, tworzę teraz własne klasy wyjątków dla wszystkich wyjątków błędów i dokumentuję je metodami, które je wyrzucają. Zmusiłbym ich do sprawdzania wyjątków w Javie.

Do niedawna próbowałem udokumentować wszystkie wyjątki, które może wywołać metoda, ale czasami tworzy nieporadną listę, którą należy udokumentować w każdej metodzie w łańcuchu wywołań, dopóki nie pokażesz, że błąd się nie zdarzy. Zamiast tego dokumentuję teraz warunki wstępne w opisach podsumowania / parametrów i nawet nie wspominam, co się stanie, jeśli nie zostaną spełnione. Chodzi o to, że ludzie nie powinni i tak wyraźnie wychwytywać tych wyjątków, więc nie ma potrzeby dokumentowania ich typów.

Aby udokumentować warunki wstępne, stwierdzenie tego, co oczywiste, po prostu tworzy niepotrzebny bałagan - jeśli przekazanie wartości null do metody nie ma oczywistego sensu, osoba dzwoniąca musi oczekiwać wyjątku, jeśli i tak przejdzie null, nawet jeśli nie jest to udokumentowane. To samo dotyczy przypadku ObjectDisposedException - ten interfejs jest tak szeroko używany, że ktoś dzwoniący Dispose byłby świadomy odpowiedzialności za zapewnienie, że nikt nie będzie kontynuował używania obiektu.

Medo42
źródło
1

Rozsądna zasada:

  1. Złap każdy wyjątek, który możesz naprawić.
  2. Złap, skomentuj i powtórz każdy wyjątek, którego nie możesz naprawić, ale możesz powiedzieć coś przydatnego.
  3. Nie wychwytuj żadnych wyjątków, których nie możesz przetworzyć ani zdiagnozować lepiej niż domyślny proces przetwarzania.

Możesz chcieć mieć najwyższej klasy moduł obsługi wyjątków, który przechwytuje wszystko, czego nie da się obsłużyć poniżej, aby nie dopuścić do upadku aplikacji, ale będzie to silnie zależeć od konkretnej aplikacji. Na przykład aplikacje iOS wolą wychwytywać jak najwięcej; aplikacja wiersza poleceń może być całkowicie OK, jeśli nie wychwytuje prawie żadnych wyjątków.

Joe McMahon
źródło
0

Nawet Java nie „dokumentuje” wszystkich wyjątków. Pomimo wymogu, aby każdy klauzula throwbyła wymieniona w throwsklauzuli, każdy wiersz kodu może wygenerować znak „a” RuntimeExceptionbez potrzeby deklarowania go w podpisie metody.

Ross Patterson
źródło
Jest to jednak sprzeczne z popularnymi wskazówkami - konkretnie, Effective Java, wyd. 2, pozycja 62 brzmi: „Udokumentuj wszystkie wyjątki zgłoszone każdą metodą”. W rzeczywistości Bloch przyznaje, że niesprawdzone wyjątki dotyczą zasadniczo błędów programistycznych, ale należy je również starannie dokumentować, ponieważ jest to „najlepszy sposób” na udokumentowanie warunków wstępnych metody. Nie jestem jednak pewien, czy się zgadzam. Jeśli dokumentuję wykonania, włączam je do umowy dotyczącej interfejsu i być może będę musiał dodać kod, aby pozostawały takie same, jeśli moja implementacja ulegnie zmianie.
Medo42,
1
Dla każdego eksperta jest kontr-ekspert. Bruce Eckel, autor dość znanego Thinking in Java , był kiedyś w obozie sprawdzonych wyjątków i zmienił strony. Jest miła dyskusja między Eckels i Anders Hejlsberg , twórcą C #, który nie sprawdził wyjątków.
Ross Patterson
0

Standardowym sposobem na to jest podejście Java. Błędy programowania powinny być odznaczonymi wyjątkami i nie powinny być wychwytywane, aby zapewnić szybką awarię. Błędy umowy są sprawdzane w wyjątkach i powinny być odpowiednio obsługiwane przez klienta.

J.Ashworth
źródło
2
Czy uważasz (np.) Obliczoną (lub wartość błędu) NULL za błąd programowania lub błąd umowy ? Idę z twoim rozróżnieniem, ale granica nie jest tak jednoznaczna :)
Andrew
Jeśli wartość null jest obliczana na podstawie przekazanego argumentu (takiego jak argument null lub coś niepoprawnego), oznacza to błąd umowy. Jeśli wartość null jest obliczana z powodu złego kodu, oznacza to błąd programowania. Jeśli null ma być obliczony, nie jest to błąd. Tak naprawdę nie widzę żadnych dwuznaczności w tym przykładzie.
J.Ashworth