Zaprojektuj na podstawie umowy z wykorzystaniem twierdzeń lub wyjątków? [Zamknięte]

123

Kiedy programując na podstawie umowy, funkcja lub metoda najpierw sprawdza, czy spełnione są jej warunki wstępne, przed rozpoczęciem pracy nad swoimi obowiązkami, prawda? Dwa najbardziej znanych sposobów na te kontrole są przez asserti przez exception.

  1. assert kończy się niepowodzeniem tylko w trybie debugowania. Aby upewnić się, że kluczowe jest (jednostkowe) przetestowanie wszystkich oddzielnych warunków wstępnych umowy, aby sprawdzić, czy faktycznie zawodzą.
  2. wyjątek kończy się niepowodzeniem w trybie debugowania i zwalniania. Ma to tę zaletę, że testowane zachowanie debugowania jest identyczne z zachowaniem wydania, ale wiąże się to z obniżeniem wydajności w czasie wykonywania.

Jak myślisz, który z nich jest lepszy?

Zobacz opublikowane pytanie tutaj

andreas buykx
źródło
3
Cały punkt związany z projektowaniem według kontraktu polega na tym, że nie musisz (i prawdopodobnie nie powinieneś) weryfikować warunków wstępnych w czasie wykonywania. Zweryfikować wejście przed przekazaniem go do metody z warunków wstępnych, to jak szanujesz swój koniec umowy. Jeśli dane wejściowe są nieprawidłowe lub naruszają zakończenie umowy, program i tak zwykle zawiedzie w ramach swoich normalnych działań (które chcesz).
void.pointer
Dobre pytanie, ale myślę, że naprawdę powinieneś zmienić zaakceptowaną odpowiedź (jak pokazują również głosy)!
DaveFar
Wiem, że wiecznie później, ale czy to pytanie powinno mieć znacznik C ++? Szukałem tej odpowiedzi, do użycia w innym języku (Delpih) i nie wyobrażam sobie żadnego języka, w którym są wyjątki i stwierdzenia, które nie podlegałyby tym samym regułom. (Wciąż uczę się wytycznych dotyczących przepełnienia stosu.)
Eric G
Bardzo zwięzła odpowiedź udzielona w tej odpowiedzi : „Innymi słowy, wyjątki dotyczą niezawodności aplikacji, podczas gdy potwierdzenia dotyczą jej poprawności”.
Shmuel Levine

Odpowiedzi:

39

Wyłączanie funkcji assert w kompilacjach wydań jest jak powiedzenie „Nigdy nie będę mieć żadnych problemów w kompilacji wydania”, co często nie ma miejsca. Dlatego asercja nie powinna być wyłączana w kompilacji wydania. Ale nie chcesz, aby kompilacja wydania ulegała awarii, gdy wystąpią błędy, prawda?

Dlatego używaj wyjątków i dobrze z nich korzystaj. Użyj dobrej, solidnej hierarchii wyjątków i upewnij się, że możesz złapać i umieścić hak na rzucaniu wyjątków w debugerze, aby go złapać, aw trybie zwolnienia możesz zrekompensować błąd, a nie zwykłą awarię. To bezpieczniejszy sposób.

coppro
źródło
4
Asercje są przydatne przynajmniej w przypadkach, gdy sprawdzenie poprawności byłoby albo nieefektywne, albo nieefektywne do prawidłowego wdrożenia.
Casebash,
89
Celem twierdzeń nie jest poprawianie błędów, ale ostrzeżenie programisty. Utrzymywanie ich włączonych w kompilacjach wydań jest bezużyteczne z tego powodu: Co byś zyskał, gdybyś miał strzelanie asercyjne? Deweloper nie będzie mógł wskoczyć i debugować go. Asercje są pomocą w debugowaniu, nie zastępują wyjątków (ani też wyjątki nie zastępują potwierdzeń). Wyjątki ostrzegają program o stanie błędu. Assert ostrzega dewelopera.
jalf,
12
Ale asercji należy używać, gdy wewnętrzne dane zostały uszkodzone po naprawieniu - jeśli asercja zostanie wyzwolona, ​​nie można przyjmować żadnych założeń dotyczących stanu programu, ponieważ oznacza to, że coś jest / nie tak /. Jeśli asercja zgasła, nie możesz zakładać, że jakiekolwiek dane są prawidłowe. Dlatego kompilacja wydania powinna zapewniać - nie informować programisty, gdzie jest problem, ale aby program mógł zamknąć się i nie ryzykować większych problemów. Program powinien po prostu zrobić wszystko, co w jego mocy, aby ułatwić późniejsze odzyskiwanie, gdy dane będą wiarygodne.
coppro
5
@jalf, Chociaż nie możesz umieścić haka w debugerze w kompilacjach wydań, możesz wykorzystać rejestrowanie, aby programiści widzieli informacje istotne dla niepowodzenia asercji. W tym dokumencie ( martinfowler.com/ieeeSoftware/failFast.pdf ) Jim Shore zwraca uwagę: „Pamiętaj, że błąd występujący w witrynie klienta przeszedł przez proces testowania. Prawdopodobnie będziesz mieć problemy z jego odtworzeniem. Te błędy to najtrudniejsze do znalezienia, a dobrze umiejscowione stwierdzenie wyjaśniające problem może zaoszczędzić dni wysiłku ”.
StriplingWarrior
5
Osobiście wolę potwierdzenia do projektowania na podstawie umów. Wyjątki są obronne i sprawdzają argumenty wewnątrz funkcji. Ponadto warunki wstępne dbc nie mówią „Nie będę działać, jeśli użyjesz wartości spoza zakresu roboczego”, ale „Nie gwarantuję, że podam poprawną odpowiedź, ale nadal mogę to zrobić”. Potwierdzenia dostarczają deweloperowi informacji zwrotnej, że wywołują funkcję z naruszeniem warunku, ale nie powstrzymują ich przed jej użyciem, jeśli czują, że wiedzą lepiej. Naruszenie może spowodować wystąpienie wyjątków, ale widzę to jako inną rzecz.
Matt_JD
194

Ogólna reguła jest taka, że ​​powinieneś używać asercji, gdy próbujesz wyłapać własne błędy, a wyjątków, gdy próbujesz wyłapać błędy innych ludzi. Innymi słowy, powinieneś używać wyjątków do sprawdzania warunków wstępnych dla publicznych funkcji API i zawsze, gdy otrzymujesz jakiekolwiek dane, które są zewnętrzne w stosunku do twojego systemu. Powinieneś używać potwierdzeń dla funkcji lub danych, które są wewnętrzne w systemie.

Dima
źródło
a co z serializacją / deserializacją w różnych modułach / aplikacjach i ostatecznie utratą synchronizacji? Mam na myśli to, że ze strony czytelnika zawsze jest moim błędem, jeśli próbuję czytać rzeczy w niewłaściwy sposób, więc zwykle używam potwierdzeń, ale z drugiej strony mam dane zewnętrzne, które ostatecznie mogą zmienić format bez powiadomienia.
Slava
Jeśli dane są zewnętrzne, należy użyć wyjątków. W tym konkretnym przypadku powinieneś raczej wychwycić te wyjątki i zająć się nimi w jakiś rozsądny sposób, zamiast po prostu pozwolić programowi umrzeć. Moja odpowiedź to praktyczna zasada, a nie prawo natury. :) Więc każdy przypadek trzeba rozpatrywać indywidualnie.
Dima
Jeśli twoja funkcja f (int * x) zawiera wiersz x-> len, to f (v), gdzie v jest zerowe, gwarantuje awarię. Co więcej, jeśli nawet wcześniej na v zostanie udowodnione, że jest zerowe, a f (v) zostanie wywołane, masz logiczną sprzeczność. To to samo, co posiadanie a / b, gdzie ostatecznie udowodniono, że b wynosi 0. Idealnie byłoby, gdyby taki kod nie mógł się skompilować. Wyłączanie sprawdzania założeń jest całkowicie głupie, chyba że problemem jest koszt sprawdzenia, ponieważ przesłania lokalizację, w której założenie zostało naruszone. Musi być przynajmniej zarejestrowany. Mimo wszystko powinieneś mieć projekt restartu po awarii.
Rob
22

Zasada, którą kieruję się jest następująca: jeśli można realistycznie uniknąć sytuacji poprzez kodowanie, użyj asercji. W przeciwnym razie użyj wyjątku.

Stwierdzenia mają na celu zapewnienie przestrzegania Umowy. Umowa musi być uczciwa, tak aby klient był w stanie zapewnić jej zgodność. Na przykład możesz określić w umowie, że adres URL musi być ważny, ponieważ reguły dotyczące tego, co jest, a co nie jest prawidłowym adresem URL, są znane i spójne.

Wyjątki dotyczą sytuacji, które są poza kontrolą zarówno klienta, jak i serwera. Wyjątek oznacza, że ​​coś poszło nie tak i nic nie można było zrobić, aby tego uniknąć. Na przykład łączność sieciowa jest poza kontrolą aplikacji, więc nie można nic zrobić, aby uniknąć błędu sieci.

Chciałbym dodać, że rozróżnienie stwierdzenie / wyjątek nie jest najlepszym sposobem, aby o tym myśleć. To, o czym naprawdę chcesz myśleć, to umowa i sposób jej egzekwowania. W powyższym przykładzie adresu URL najlepiej jest mieć klasę, która zawiera adres URL i ma wartość Null lub prawidłowy adres URL. Jest to konwersja ciągu znaków na adres URL, który wymusza kontrakt, a wyjątek jest generowany, jeśli jest nieprawidłowy. Metoda z parametrem adresu URL jest znacznie bardziej przejrzysta niż metoda z parametrem typu String i asercją określającą adres URL.

Ged Byrne
źródło
6

Potwierdzenia służą do wychwytywania czegoś, co programista zrobił źle (nie tylko dla Ciebie - również innego programisty w Twoim zespole). Jeśli rozsądne jest, że błąd użytkownika może stworzyć ten warunek, powinien to być wyjątek.

Pomyśl też o konsekwencjach. Asercja zazwyczaj wyłącza aplikację. Jeśli istnieje realistyczne oczekiwanie, że stan można wyleczyć, prawdopodobnie powinieneś użyć wyjątku.

Z drugiej strony, jeśli problem może wynikać tylko z błędu programisty, użyj asercji, ponieważ chcesz wiedzieć o tym jak najszybciej. Wyjątek może zostać złapany i obsłużony, a nigdy się o nim nie dowiesz. I tak, powinieneś wyłączyć potwierdzenia w kodzie wersji, ponieważ tam chcesz, aby aplikacja została przywrócona, jeśli jest najmniejsza szansa, że ​​może. Nawet jeśli stan twojego programu jest głęboko uszkodzony, użytkownik może po prostu zachować swoją pracę.

DJClayworth
źródło
5

Nie jest do końca prawdą, że „assert kończy się niepowodzeniem tylko w trybie debugowania”.

W Object Oriented Software Construction, 2. wydanie Bertranda Meyera, autor zostawia otwarte drzwi do sprawdzenia warunków wstępnych w trybie wydania. W takim przypadku to, co się dzieje, gdy asercja się nie powiedzie, to ... zgłaszany jest wyjątek naruszenia asercji! W tym przypadku nie ma wyjścia z sytuacji: można by jednak zrobić coś pożytecznego, a jest to automatyczne wygenerowanie raportu o błędach oraz, w niektórych przypadkach, ponowne uruchomienie aplikacji.

Motywacją jest to, że testowanie warunków wstępnych jest zazwyczaj tańsze niż niezmienników i warunków końcowych, a w niektórych przypadkach poprawność i „bezpieczeństwo” w kompilacji wydania są ważniejsze niż szybkość. tzn. dla wielu aplikacji nie chodzi o szybkość, ale o solidność (zdolność programu do bezpiecznego zachowania, gdy jego zachowanie jest nieprawidłowe, tj. gdy umowa jest zerwana).

Czy zawsze należy pozostawić włączone sprawdzanie warunków wstępnych? To zależy. To zależy od Ciebie. Nie ma uniwersalnej odpowiedzi. Jeśli tworzysz oprogramowanie dla banku, może być lepiej przerwać wykonywanie alarmującym komunikatem niż przelać 1 000 000 USD zamiast 1 000 USD. Ale co, jeśli programujesz grę? Może potrzebujesz całej prędkości, jaką możesz uzyskać, a jeśli ktoś zdobędzie 1000 punktów zamiast 10 z powodu błędu, którego nie złapały warunki wstępne (ponieważ nie są włączone), pech.

W obu przypadkach idealnie byłoby, gdybyś złapał ten błąd podczas testowania, a znaczną część testów powinieneś wykonać z włączonymi asercjami. Omówiono tutaj najlepszą politykę dla tych rzadkich przypadków, w których warunki wstępne zawodzą w kodzie produkcyjnym w scenariuszu, który nie został wcześniej wykryty z powodu niepełnego testowania.

Podsumowując, możesz mieć potwierdzenia i nadal automatycznie uzyskiwać wyjątki , jeśli zostawisz je włączone - przynajmniej w Eiffel. Myślę, że aby zrobić to samo w C ++, musisz wpisać to sam.

Zobacz także: Kiedy asercje powinny pozostać w kodzie produkcyjnym?

Daniel Daranas
źródło
1
Twój punkt jest zdecydowanie ważny. W SO nie określono konkretnego języka - w przypadku C # standardowym potwierdzeniem jest System.Diagnostics.Debug.Assert, który kończy się niepowodzeniem w kompilacji debugowania i zostanie usunięty w czasie kompilacji w kompilacji wydania.
jojo,
2

Był duży wątek dotyczący włączania / wyłączania asercji w kompilacjach wydań na comp.lang.c ++. Moderated, który, jeśli masz kilka tygodni, możesz zobaczyć, jak różne są opinie na ten temat. :)

W przeciwieństwie do coppro uważam, że jeśli nie masz pewności, że asercja może zostać wyłączona w kompilacji wydania, to nie powinna była to być asercja. Asercje mają chronić przed zerwaniem niezmienników programu. W takim przypadku, jeśli chodzi o klienta twojego kodu, będzie jeden z dwóch możliwych wyników:

  1. Zgiń z jakąś awarią systemu operacyjnego, co spowoduje wezwanie do przerwania. (Bez potwierdzenia)
  2. Zgiń przez bezpośrednie wezwanie do przerwania. (Z potwierdzeniem)

Nie ma różnicy dla użytkownika, jednak możliwe jest, że asercje dodają niepotrzebny koszt wydajności do kodu, który jest obecny w zdecydowanej większości uruchomień, w których kod nie zawodzi.

Odpowiedź na pytanie w rzeczywistości znacznie bardziej zależy od tego, kim będą klienci API. Jeśli piszesz bibliotekę udostępniającą API, potrzebujesz jakiejś formy mechanizmu, aby powiadomić klientów, że niewłaściwie korzystali z API. Jeśli nie dostarczysz dwóch wersji biblioteki (jedna z potwierdzeniami, druga bez), to assert jest bardzo mało prawdopodobne, aby był to właściwy wybór.

Osobiście jednak nie jestem pewien, czy wybrałbym wyjątki w tym przypadku. Wyjątki są lepiej dostosowane do sytuacji, w których może mieć miejsce odpowiednia forma zdrowienia. Na przykład może to oznaczać, że próbujesz przydzielić pamięć. Po złapaniu wyjątku „std :: bad_alloc” może być możliwe zwolnienie pamięci i spróbuj ponownie.

Richard Corden
źródło
2

Przedstawiłem tutaj mój pogląd na stan rzeczy: Jak sprawdzić stan wewnętrzny obiektu? . Ogólnie rzecz biorąc, dochodź swoich roszczeń i rzucaj je za naruszenie przez innych. Aby wyłączyć potwierdzenia w kompilacjach wydań, możesz wykonać następujące czynności:

  • Wyłącz potwierdzenia dla kosztownych kontroli (np. Sprawdzanie, czy zakres jest uporządkowany)
  • Pozostaw włączone trywialne sprawdzenia (takie jak sprawdzanie wskaźnika pustego lub wartości logicznej)

Oczywiście w kompilacjach wydań nieudane asercje i nieprzechwycone wyjątki powinny być obsługiwane w inny sposób niż w kompilacjach debugowania (gdzie można po prostu wywołać std :: abort). Zapisz gdzieś dziennik błędu (prawdopodobnie do pliku), powiedz klientowi, że wystąpił błąd wewnętrzny. Klient będzie mógł przesłać Ci plik dziennika.

Johannes Schaub - litb
źródło
1

pytasz o różnicę między błędami czasu projektowania i wykonywania.

potwierdzenia to powiadomienia „hej programisto, to jest zepsute”, mają przypominać o błędach, których nie zauważyłeś, kiedy się pojawiły.

wyjątkami są powiadomienia `` hej użytkowniku, coś poszło nie tak '' (oczywiście możesz kodować, aby je złapać, aby użytkownik nigdy nie został poinformowany), ale są one zaprojektowane tak, aby pojawiały się w czasie wykonywania, gdy użytkownik Joe korzysta z aplikacji.

Jeśli więc myślisz, że możesz usunąć wszystkie swoje błędy, używaj tylko wyjątków. Jeśli uważasz, że nie możesz ..... użyj wyjątków. Nadal można używać potwierdzeń debugowania, aby oczywiście zmniejszyć liczbę wyjątków.

Nie zapominaj, że wiele warunków wstępnych to dane dostarczone przez użytkownika, więc będziesz potrzebować dobrego sposobu poinformowania użytkownika, że ​​jego dane nie były dobre. Aby to zrobić, często będziesz musiał zwrócić dane o błędach w dół stosu wywołań do bitów, z którymi ma do czynienia. Potwierdzenia nie będą wtedy przydatne - podwójnie, więc jeśli Twoja aplikacja jest n-warstwowa.

Na koniec nie użyłbym żadnego - kody błędów są znacznie lepsze w przypadku błędów, które Twoim zdaniem będą występować regularnie. :)

gbjbaanb
źródło
0

Wolę ten drugi. Chociaż twoje testy mogły działać dobrze, Murphy mówi, że coś nieoczekiwanego pójdzie nie tak. Tak więc, zamiast uzyskać wyjątek w rzeczywistym wywołaniu błędnej metody, w końcu śledzisz NullPointerException (lub równoważny) o 10 ramek stosu głębiej.

jdmichal
źródło
0

Poprzednie odpowiedzi są poprawne: używaj wyjątków dla publicznych funkcji API. Jedynym przypadkiem, w którym możesz chcieć nagiąć tę regułę, jest sytuacja, gdy sprawdzenie jest kosztowne obliczeniowo. W takim przypadku możesz umieścić to w asercie.

Jeśli uważasz, że naruszenie tego warunku wstępnego jest prawdopodobne, zachowaj go jako wyjątek lub zreformuj warunek wstępny.

Mike Elkins
źródło
0

Powinieneś użyć obu. Potwierdzenia są dla Twojej wygody jako programisty. Wyjątki obejmują rzeczy, które przeoczyłeś lub których się nie spodziewałeś w czasie działania.

Mam polubili funkcji raportowania błędów GLib za zamiast zwykły stary twierdzi. Zachowują się jak instrukcje assert, ale zamiast zatrzymywać program, po prostu zwracają wartość i pozwalają programowi kontynuować. Działa zaskakująco dobrze, a jako bonus możesz zobaczyć, co dzieje się z resztą programu, gdy funkcja nie zwraca „tego, co powinna”. Jeśli się zawiesza, wiesz, że sprawdzanie błędów jest luźne w innym miejscu.

W moim ostatnim projekcie użyłem tego stylu funkcji do zaimplementowania sprawdzania warunków wstępnych, a jeśli jedna z nich zawiodła, wydrukowałem ślad stosu w pliku dziennika, ale nadal działałem. Zaoszczędziło mi mnóstwo czasu na debugowanie, gdy inne osoby napotkałyby problem podczas uruchamiania mojej kompilacji debugowania.

#ifdef DEBUG
#define RETURN_IF_FAIL(expr)      do {                      \
 if (!(expr))                                           \
 {                                                      \
     fprintf(stderr,                                        \
        "file %s: line %d (%s): precondition `%s' failed.", \
        __FILE__,                                           \
        __LINE__,                                           \
        __PRETTY_FUNCTION__,                                \
        #expr);                                             \
     ::print_stack_trace(2);                                \
     return;                                                \
 };               } while(0)
#define RETURN_VAL_IF_FAIL(expr, val)  do {                         \
 if (!(expr))                                                   \
 {                                                              \
    fprintf(stderr,                                             \
        "file %s: line %d (%s): precondition `%s' failed.",     \
        __FILE__,                                               \
        __LINE__,                                               \
        __PRETTY_FUNCTION__,                                    \
        #expr);                                                 \
     ::print_stack_trace(2);                                    \
     return val;                                                \
 };               } while(0)
#else
#define RETURN_IF_FAIL(expr)
#define RETURN_VAL_IF_FAIL(expr, val)
#endif

Gdybym potrzebował sprawdzania argumentów w czasie wykonywania, zrobiłbym to:

char *doSomething(char *ptr)
{
    RETURN_VAL_IF_FAIL(ptr != NULL, NULL);  // same as assert(ptr != NULL), but returns NULL if it fails.
                                            // Goes away when debug off.

    if( ptr != NULL )
    {
       ...
    }

    return ptr;
}
indyw
źródło
Chyba nie widziałem w pytaniu OP niczego związanego z C ++. Uważam, że nie powinno się tego uwzględniać w Twojej odpowiedzi.
ForceMagic
@ForceMagic: Pytanie miało tag C ++ w 2008 r., Kiedy zamieściłem tę odpowiedź, iw rzeczywistości tag C ++ został usunięty zaledwie 5 godzin temu. Niezależnie od tego, kod ilustruje koncepcję niezależną od języka.
indywid.
0

Spróbowałem zsyntetyzować tutaj kilka innych odpowiedzi z własnymi poglądami.

Użyj asercji w przypadkach, w których chcesz wyłączyć je w środowisku produkcyjnym, błądząc w kierunku pozostawienia ich w środku. Jedynym prawdziwym powodem wyłączenia w środowisku produkcyjnym, ale nie w fazie rozwoju, jest przyspieszenie działania programu. W większości przypadków to przyspieszenie nie będzie znaczące, ale czasami kod jest krytyczny czasowo lub test jest kosztowny obliczeniowo. Jeśli kod ma kluczowe znaczenie dla misji, wyjątki mogą być najlepsze pomimo spowolnienia.

Jeśli istnieje realna szansa na odzyskanie, użyj wyjątku, ponieważ potwierdzenia nie są przeznaczone do odzyskiwania. Na przykład kod rzadko jest przeznaczony do odtwarzania po błędach programowania, ale jest przeznaczony do odzyskiwania po takich czynnikach, jak awarie sieci lub zablokowane pliki. Błędy nie powinny być traktowane jako wyjątki po prostu poza kontrolą programisty. Raczej przewidywalność tych błędów w porównaniu z błędami w kodowaniu sprawia, że ​​łatwiej je naprawić.

Ponownie argument, że łatwiej jest debugować asercje: ślad stosu z prawidłowo nazwanego wyjątku jest tak łatwy do odczytania, jak asercja. Dobry kod powinien wychwytywać tylko określone typy wyjątków, więc wyjątki nie powinny pozostać niezauważone z powodu wychwycenia. Myślę jednak, że Java czasami zmusza cię do wyłapania wszystkich wyjątków.

Casebash
źródło
0

Według mnie ogólną zasadą jest używanie wyrażeń asercji do znajdowania błędów wewnętrznych i wyjątków błędów zewnętrznych. Możesz wiele skorzystać z poniższej dyskusji Grega tutaj .

Wyrażenia asertu służą do znajdowania błędów programistycznych: błędów w samej logice programu lub błędów w odpowiadającej mu implementacji. Warunek potwierdzenia sprawdza, czy program pozostaje w zdefiniowanym stanie. „Stan zdefiniowany” to zasadniczo taki, który jest zgodny z założeniami programu. Zauważ, że „stan zdefiniowany” dla programu nie musi być „stanem idealnym” ani nawet „stanem zwykłym”, ani nawet „stanem użytecznym”, ale więcej o tym ważnym punkcie później.

Aby zrozumieć, jak asercje pasują do programu, rozważ procedurę w programie C ++, która ma zamiar wyłuskać wskaźnik. Czy teraz procedura rutynowa powinna sprawdzić, czy wskaźnik ma wartość NULL przed wyłuskiwaniem, czy też powinna zapewnić, że wskaźnik nie ma wartości NULL, a następnie kontynuować i wyłuskiwać go niezależnie?

Wyobrażam sobie, że większość programistów chciałaby zrobić jedno i drugie, dodać potwierdzenie, ale także sprawdzić wskaźnik pod kątem wartości NULL, aby nie zawiesić się, gdyby potwierdzony warunek zawiódł. Pozornie wykonanie zarówno testu, jak i sprawdzenia może wydawać się najmądrzejszą decyzją

W przeciwieństwie do warunków zapewnianych przez program, obsługa błędów (wyjątków) programu nie odnosi się do błędów w programie, ale do danych wejściowych, które program uzyskuje ze swojego środowiska. Są to często „błędy” po stronie użytkownika, na przykład próba zalogowania się do konta bez wpisywania hasła. I nawet jeśli błąd może uniemożliwić pomyślne zakończenie zadania programu, nie ma awarii programu. Program nie loguje się do użytkownika bez hasła z powodu błędu zewnętrznego - błędu po stronie użytkownika. Jeśli okoliczności były inne, a użytkownik wpisał poprawne hasło, a program go nie rozpoznał; wtedy chociaż wynik byłby nadal taki sam, porażka należałaby teraz do programu.

Cel obsługi błędów (wyjątków) jest dwojaki. Pierwszym jest poinformowanie użytkownika (lub innego klienta), że wykryto błąd w danych wejściowych programu i co to oznacza. Drugim celem jest przywrócenie aplikacji po wykryciu błędu do dobrze zdefiniowanego stanu. Zauważ, że sam program nie jest w błędzie w tej sytuacji. To prawda, że ​​program może być w stanie nieidealnym lub nawet w stanie, w którym nie można nic zrobić, ale nie ma błędu programistycznego. Wręcz przeciwnie, ponieważ stan przywracania po błędzie jest taki, jaki przewiduje projekt programu, jest to stan, z którym program sobie poradzi.

PS: możesz sprawdzić podobne pytanie: wyjątek kontra asercja .

herohuyongtao
źródło
-1

Zobacz także to pytanie :

W niektórych przypadkach potwierdzenia są wyłączone podczas budowania do wydania. Możesz nie mieć nad tym kontroli (w przeciwnym razie możesz budować z włączonymi potwierdzeniami), więc dobrym pomysłem może być zrobienie tego w ten sposób.

Problem z „poprawianiem” wartości wejściowych polega na tym, że wywołujący nie otrzyma tego, czego oczekuje, a to może prowadzić do problemów lub nawet awarii w całkowicie różnych częściach programu, czyniąc debugowanie koszmarem.

Zwykle rzucam wyjątek w instrukcji if, aby przejąć rolę assert w przypadku, gdy są wyłączone

assert(value>0);
if(value<=0) throw new ArgumentOutOfRangeException("value");
//do stuff
Rik
źródło