Wzór umowy z wykorzystaniem twierdzeń czy WYJĄTKÓW?

Podczas programowania przez kontrakt funkcja lub metoda najpierw sprawdza, czy jej warunki wstępne są spełnione, przed rozpoczęciem pracy nad swoimi obowiązkami, prawda? Dwa najważniejsze sposoby przeprowadzania tych kontroli To assert i exception.

  1. assert nie powiedzie się tylko w trybie debugowania. Aby upewnić się, że konieczne jest (jednostkowe) przetestowanie wszystkich warunków wstępnych odrębnych umów, aby sprawdzić, czy faktycznie nie zawiodą.
  2. wyjątek nie działa w trybie debugowania i Wydania. Ma to tę zaletę, że testowane debugowanie zachowanie jest identyczne z zachowaniem release, ale wiąże się z karą za wydajność wykonania.
Który według ciebie jest lepszy?

Zobacz pytanie tutaj

Author: Community, 2008-09-22

14 answers

Wyłączenie assert w release buildach jest jak powiedzenie "Nigdy Nie będę miał żadnych problemów w release buildzie", co często nie ma miejsca. Dlatego assert nie powinien być wyłączany w kompilacji release. Ale nie chcesz, aby kompilacja wersji zawieszała się za każdym razem, gdy pojawiają się błędy, prawda?

Więc używaj wyjątków i używaj ich dobrze. Użyj dobrej, solidnej hierarchii wyjątków i upewnij się, że złapiesz i możesz umieścić hook na rzuceniu wyjątku w debugerze, aby go złapać, a w trybie release możesz / align = "center" bgcolor = "# e0ffe0 " / cesarz chin / / align = center / Tak będzie bezpieczniej.

 41
Author: coppro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-30 00:43:53

Zasada jest taka, że powinieneś używać twierdzeń, gdy próbujesz wyłapać własne błędy, oraz WYJĄTKÓW, gdy próbujesz wyłapać błędy innych ludzi. Innymi słowy, należy użyć WYJĄTKÓW, aby sprawdzić warunki wstępne dla funkcji publicznego API i za każdym razem, gdy otrzymasz jakiekolwiek dane, które są zewnętrzne do Twojego systemu. Należy używać asserts dla funkcji lub danych wewnętrznych systemu.

 191
Author: Dima,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-22 20:06:07

Zasada, którą kieruję się jest następująca: jeśli sytuacja może być realistycznie uniknięta przez kodowanie, użyj twierdzenia. W przeciwnym razie użyj wyjątku.

Twierdzenia mają na celu zapewnienie, że umowa jest przestrzegana. Umowa musi być uczciwa, tak aby klient był w stanie zapewnić jej zgodność. Na przykład w umowie można określić, że adres URL musi być ważny, ponieważ zasady dotyczące tego, co jest, a co nie jest poprawnym 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 znajduje się poza kontrolą aplikacji, więc nie można nic zrobić, aby uniknąć błędu sieciowego.

Chciałbym dodać, że rozróżnienie twierdzenia / wyjątku nie jest najlepszym sposobem na myślenie o tym. To, o czym naprawdę chcesz myśleć, to umowa i jak może być egzekwowane. W moim przykładzie URL powyżej najlepiej jest mieć klasę, która hermetyzuje adres URL i jest albo Null, albo poprawnym adresem URL. Jest to konwersja ciągu znaków na adres URL, która wymusza umowę, a wyjątek jest wyrzucany, jeśli jest nieważny. Metoda z parametrem URL jest znacznie jaśniejsza niż metoda z parametrem String i twierdzeniem, które określa adres URL.

 22
Author: Ged Byrne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-23 20:24:33

Twierdzenia są po to, aby złapać coś, co deweloper zrobił źle (nie tylko Ty - inny deweloper w Twoim zespole również). Jeśli jest uzasadnione, że błąd użytkownika może stworzyć ten warunek, powinien to być wyjątek.

Podobnie pomyśl o konsekwencjach. Asert zazwyczaj zamyka aplikację. Jeśli istnieje jakiekolwiek realne oczekiwanie, że warunek można odzyskać, prawdopodobnie należy użyć wyjątku.

Z drugiej strony, jeśli problem może tylko być spowodowane błędem programisty następnie użyj assert, ponieważ chcesz wiedzieć o nim jak najszybciej. Wyjątek może być złapany i obsługiwany, a ty nigdy się o tym nie dowiesz. I tak, powinieneś wyłączyć twierdzenia w kodzie wydania, ponieważ chcesz, aby aplikacja odzyskała, jeśli istnieje najmniejsza szansa, że może. Nawet jeśli stan twojego programu jest głęboko uszkodzony, użytkownik może być w stanie zapisać swoją pracę.

 6
Author: DJClayworth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-25 21:24:23

Nie jest do końca prawdą, że " assert nie powiedzie się tylko w trybie debugowania."

In Object Oriented Software Construction, 2nd Edition by Bertrand Meyer, autor pozostawia otwarte drzwi do sprawdzenia warunków wstępnych w trybie release. W takim przypadku to, co się dzieje, gdy twierdzenie się nie powiedzie, jest takie... wyjątek naruszenia twierdzenia jest podniesiony! W tym przypadku nie ma poprawy sytuacji: można jednak zrobić coś pożytecznego, a polega to na automatycznym generowaniu raportu o błędzie i, w w niektórych przypadkach, aby ponownie uruchomić aplikację.

Motywacją jest to, że Warunki wstępne są zwykle tańsze do przetestowania niż niezmienniki i warunki postkondensacyjne, a w niektórych przypadkach poprawność i "bezpieczeństwo" w kompilacji wydania są ważniejsze niż szybkość. tzn. dla wielu aplikacji szybkość nie jest problemem, ale solidność (zdolność programu do zachowania się w bezpieczny sposób, gdy jego zachowanie nie jest poprawne, tzn. gdy kontrakt jest zerwany) jest.

Czy zawsze zostawić kontrola wstępna włączona? To zależy. To zależy od Ciebie. Nie ma uniwersalnej odpowiedzi. Jeśli tworzysz oprogramowanie dla banku, może lepiej przerwać wykonanie alarmującym Komunikatem niż przelać 1 000 000 $zamiast 1 000$. A co jeśli programujesz grę? Może potrzebujesz całej prędkości, a jeśli ktoś dostanie 1000 punktów zamiast 10 z powodu błędu, którego warunki wstępne nie złapały( bo nie są włączone), to pech.

W obu przypadkach należy najlepiej mieć złapany ten błąd podczas testowania i powinieneś zrobić znaczną część testów z włączonymi twierdzeniami. Omawiane jest tutaj, jaka jest najlepsza polityka dla tych rzadkich przypadków, w których warunki wstępne zawodzą w kodzie produkcyjnym w scenariuszu, który nie został wykryty wcześniej z powodu niekompletnych testów.

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

Zobacz także: kiedy twierdzenia powinny pozostać w kodzie produkcyjnym?

 4
Author: Daniel Daranas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:25:53

Pojawił się ogromny Wątek dotyczący włączania/wyłączania twierdzeń w Release buildach na comp.lang.c++.moderowany, który jeśli masz kilka tygodni możesz zobaczyć jak zróżnicowane są opinie na ten temat. :)

W przeciwieństwie do coppro , uważam, że jeśli nie jesteś pewien, czy twierdzenie można wyłączyć w kompilacji release, to nie powinno to być twierdzenie. Twierdzenia mają chronić przed złamaniem niezmienników programu. W takim przypadku, o ile klient Twój kod obawia się, że będzie jeden z dwóch możliwych wyników:

  1. umrzeć z jakiegoś rodzaju awarii typu OS, powodując wezwanie do przerwania. (Bez twierdzenia)
  2. / Align = "center" bgcolor = "# e0ffe0 " / cesarz chin / / align = center / (With assert)

Nie ma różnicy dla użytkownika, jednak możliwe jest, że twierdzenia dodają niepotrzebny koszt wydajności w kodzie, który jest obecny w zdecydowanej większości uruchomień, gdzie kod nie zawiedzie.

Odpowiedź na pytanie faktycznie zależy znacznie bardziej od tego, kim będą klienci API. Jeśli piszesz bibliotekę dostarczającą API, potrzebujesz jakiegoś mechanizmu powiadamiającego klientów o nieprawidłowym użyciu API. Jeśli nie dostarczysz dwóch wersji biblioteki (jednej z asserts, drugiej bez), assert jest mało prawdopodobnym wyborem.

Osobiście jednak nie jestem pewien, czy w tej sprawie też wybrałbym wyjątki. Wyjątki lepiej nadają się tam, gdzie odpowiednia forma odzyskiwanie może nastąpić. Na przykład może się zdarzyć, że próbujesz przydzielić pamięć. Po złapaniu wyjątku 'STD:: bad_alloc' możliwe jest zwolnienie pamięci i ponowne spróbowanie.

 2
Author: Richard Corden,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:17:53

Przedstawiłem tutaj swój pogląd na stan rzeczy: Jak sprawdzić stan wewnętrzny obiektu? . Ogólnie rzecz biorąc, dochodzić swoich roszczeń i rzucać za naruszenie przez innych. Aby wyłączyć asserts w release builds, możesz zrobić:

  • Wyłącz twierdzenia dla kosztownych kontroli (np. sprawdzanie, czy zakres jest uporządkowany)
  • Włącz trywialne sprawdzanie (np. sprawdzanie wskaźnika null lub wartości logicznej)

Oczywiście w release buildach, nieudane twierdzenia i nieużywane wyjątki powinny być obsługiwane w inny sposób niż w kompilacjach debug (gdzie może po prostu wywołać STD::abort). Napisz gdzieś dziennik błędu (ewentualnie do pliku), powiedz klientowi, że wystąpił błąd wewnętrzny. Klient będzie mógł wysłać plik dziennika.

 2
Author: Johannes Schaub - litb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:46:51

Pytasz o różnicę między błędami w czasie projektowania a czasem wykonania.

Twierdzenia są' Hej programista, to jest zepsute ' powiadomienia, są tam, aby przypomnieć o błędach, nie zauważyłeś, gdy one się wydarzyły.

Wyjątkami są powiadomienia "hey user, somethings gone wrong" (oczywiście możesz kodować, aby je złapać, aby użytkownik nigdy nie został poinformowany) , ale są one zaprojektowane tak, aby wystąpić w czasie działania, gdy użytkownik Joe korzysta z aplikacji.

Więc, jeśli myślisz, że możesz uzyskać wszystkie twoje błędy, używaj tylko WYJĄTKÓW. Jeśli myślisz, że nie możesz..... użyj WYJĄTKÓW. Nadal można używać twierdzeń debug, aby oczywiście zmniejszyć liczbę WYJĄTKÓW.

Nie zapominaj, że wiele warunków wstępnych będzie danymi dostarczonymi 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 ze stosu wywołań do bitów, z którymi wchodzi w interakcję. Twierdzenia nie będą przydatne wtedy-podwójnie więc jeśli aplikacja jest N-poziom.

Na koniec, użyłbym żadnego - kody błędów są o wiele lepsze dla błędów, które uważasz, że będą występować regularnie. :)

 1
Author: gbjbaanb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-22 20:05:37

Wolę drugą. Chociaż twoje testy mogły się udać, Murphy mówi, że coś nieoczekiwanego pójdzie nie tak. Tak więc, zamiast uzyskiwać wyjątek podczas rzeczywistego błędnego wywołania metody, w końcu wyśledzisz 10 klatek stosu głębiej.

 0
Author: jdmichal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-22 20:08:12

Poprzednie odpowiedzi są poprawne: użyj wyjątków dla publicznych funkcji API. Jedynym momentem, w którym możesz chcieć nagiąć tę zasadę, jest czas, w którym czek jest kosztowny obliczeniowo. W takim przypadku, można umieścić go w twierdzeniu.

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

 0
Author: Mike Elkins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-22 20:51:18

Powinieneś używać obu. Twierdzenia są dla Twojej wygody jako deweloper. Wyjątki wychwytują rzeczy, które przegapiłeś lub których się nie spodziewałeś podczas wykonywania.

Polubiłem funkcje raportowania błędów gliba zamiast zwykłych, starych twierdzeń. Zachowują się jak instrukcje assert, ale zamiast zatrzymać 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 "to, co powinno". Jeśli ulegnie awarii, wiesz, że sprawdzanie błędów jest niedokładne w innym miejscu.

W moim ostatnim projekcie wykorzystałem ten styl funkcji do zaimplementowania sprawdzania warunków wstępnych, a jeśli jedna z nich się nie powiedzie, wydrukowałbym ślad stosu do pliku dziennika, ale nie przestawał działać. Zaoszczędziłem mnóstwo czasu debugowania, gdy inni ludzie napotkaliby problem podczas uruchamiania mojego kompilatora 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, 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;
}
 0
Author: indiv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-22 21:23:13

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

Użyj twierdzeń w przypadkach, w których chcesz je wyłączyć w produkcji, błądząc w kierunku pozostawienia ich w środku. Jedynym prawdziwym powodem wyłączenia w produkcji, ale nie w rozwoju, jest przyspieszenie programu. W większości przypadków przyspieszenie to nie będzie znaczące, ale czasami kod ma krytyczne znaczenie czasowe lub test jest kosztowny obliczeniowo. Jeśli kod jest krytyczny, to wyjątki mogą być najlepsze pomimo powolnego na ziemię.

Jeśli istnieje jakakolwiek realna szansa na odzyskanie, użyj wyjątku, ponieważ twierdzenia nie są przeznaczone do odzyskania. Na przykład kod rzadko jest przeznaczony do odzyskiwania 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 tylko dlatego, że znajdują się poza kontrolą programisty. Raczej przewidywalność tych błędów, w porównaniu z błędami kodowania, czyni je bardziej przyjaznymi dla powrót do zdrowia.

Ponownie argument, że łatwiej jest debugować twierdzenia: ślad stosu z poprawnie nazwanego wyjątku jest tak łatwy do odczytania jak twierdzenie. Dobry kod powinien wyłapywać tylko określone typy WYJĄTKÓW, więc wyjątki nie powinny pozostać niezauważone ze względu na złapanie. Myślę jednak, że Java czasami zmusza do wyłapywania wszystkich wyjątków.

 0
Author: Casebash,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-25 07:40:23

Zobacz też to pytanie :

I niektóre przypadki, twierdzenia są wyłączone podczas budowania do wydania. Możesz nie mieć nad tym kontroli (w przeciwnym razie można budować z NA), więc dobrym pomysłem może być zrobienie tego w ten sposób.

Problem z "poprawieniem" wartości wejściowych polega na tym, że wywołujący nie dostać tego, czego oczekują, a to może prowadzić do problemów, a nawet zawiesza się w zupełnie różnych częściach programu, co powoduje, że debugowanie koszmar.

Zwykle rzucam wyjątek w instrukcji if, aby przejąć rolę of the assert in case they are disabled

assert(value>0);
if(value<=0) throw new ArgumentOutOfRangeException("value");
//do stuff
 0
Author: Rik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:29

Dla mnie zasadą jest, że używaj wyrażeń assert do znajdowania błędów wewnętrznych i wyjątków dla błędów zewnętrznych. Możesz Wiele skorzystać z poniższej dyskusji Grega z tutaj .

Wyrażenia Assert są używane do znajdowania błędów programistycznych: błędów w samej logice programu lub błędów w jego odpowiedniej implementacji. Warunek assert sprawdza, czy program pozostaje w określonym stanie. "Stan zdefiniowany" to w zasadzie taki, który zgadza się z założenia programu. Zauważ, że" zdefiniowany stan "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ż rutynę w program w języku C++, który ma wyzerować wskaźnik. Teraz powinien rutynowe badanie, czy wskaźnik jest NULL przed dereferencją, lub należy stwierdzić, że wskaźnik nie jest NULL, a następnie iść do przodu i bez względu na to?

Wyobrażam sobie, że większość programistów chciałaby zrobić jedno i drugie, dodać, ale także sprawdzić wskaźnik dla wartości NULL, aby nie zawiesić w przypadku niepowodzenia testu. Na powierzchni, wykonując zarówno test i kontrola mogą wydawać się najmądrzejszą decyzją

W przeciwieństwie do warunków programu obsługa błędów (WYJĄTKÓW) nie odnosi się do błędów w programie, ale aby wprowadzić program uzyskuje z jego środowisko. Są to często "błędy" z czyjejś strony, np. użytkownik próba zalogowania się do konta bez wpisywania hasła. Oraz nawet jeśli błąd może uniemożliwić pomyślne ukończenie programu zadanie, nie ma awarii programu. Program nie zaloguje użytkownika bez hasła z powodu błędu zewnętrznego - błąd użytkownika część. Gdyby okoliczności były inne, a użytkownik wpisał w poprawne hasło i program nie rozpoznał go; wtedy chociaż wynik nadal będzie taka sama, porażka będzie teraz należeć do program.

Cel obsługi błędów (wyjątków) jest dwojaki. Pierwszym jest komunikowanie się użytkownikowi (lub innemu klientowi), że błąd we wprowadzaniu programu ma wykryto i co to oznacza. Drugim celem jest przywrócenie aplikacja po wykryciu błędu, do dobrze zdefiniowanego stanu. Uwaga że sam program nie popełnia błędu w tej sytuacji. Granted, the program może być w stanie nie idealnym, lub nawet stan, w którym można nic użytecznego, ale nie ma błędów programistycznych. Wręcz przeciwnie., ponieważ stan odzyskiwania błędów jest przewidywany przez program projekt, to jest to, że program może obsłużyć.

PS: możesz sprawdzić podobne pytanie: wyjątek Vs twierdzenie .

 0
Author: herohuyongtao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:29