Jak mogę poprawić sprawdzanie błędów i obsługę?

13

Ostatnio staram się zrozumieć, jaka jest właściwa ilość sprawdzania i jakie są właściwe metody.

Mam kilka pytań na ten temat:

Jaki jest właściwy sposób sprawdzania błędów (złe dane wejściowe, złe stany itp.)? Czy lepiej jest jawnie sprawdzać błędy, czy używać funkcji takich jak asercje, które można zoptymalizować z końcowego kodu? Mam wrażenie, że jawnie sprawdzam bałagan w programie z dużą ilością dodatkowego kodu, który i tak nie powinien być wykonywany w większości sytuacji - i nie wspominając o tym, że większość błędów kończy się niepowodzeniem przerwania / wyjścia. Po co zaśmiecać funkcję wyraźnymi kontrolami, aby po prostu przerwać? Szukałem stwierdzeń w porównaniu do jawnego sprawdzania błędów i nie znalazłem niewiele, aby naprawdę wyjaśnić, kiedy to zrobić.

Większość mówi „użyj twierdzeń, aby sprawdzić błędy logiczne, i użyj jawnych kontroli, aby sprawdzić inne błędy”. Wydaje się, że to nie prowadzi nas tak daleko. Czy moglibyśmy powiedzieć, że jest to wykonalne:

Malloc returning null, check explictly
API user inserting odd input for functions, use asserts

Czy to poprawiłoby mnie w sprawdzaniu błędów? Co jeszcze mogę zrobić? Naprawdę chcę poprawić i napisać lepszy, „profesjonalny” kod.

Zaraz
źródło
3
Dobre pytanie, ale myślę, że może lepiej pasować do jednej z siostrzanych stron (programistów?).
Dzięki, nie byłem pewien. Pomyślałem, że skoro było to dość związane z kodem, SO byłoby w porządku.
Anon
3
Prosta odpowiedź brzmi: „Właśnie dlatego wymyślono wyjątki. Zdobądź lepszy język”.
DeadMG
1
@DeadMG: setjmp/ longjmpsą dostępne w C, więc nie potrzebujesz nowego języka.
user786653
3
@DeadMG: Ktoś, kto nie może poprawnie sprawdzić błędu C, ma szansę na śnieżki w piekle, poprawnie obsługując wyjątek C ++ ...
Koder

Odpowiedzi:

4

Najłatwiejszym sposobem, aby stwierdzić różnicę, jest ustalenie, czy warunek błędu zostanie wprowadzony w czasie kompilacji, czy w czasie wykonywania. Jeśli problem polega na tym, że programista w jakiś sposób źle używa tej funkcji, spraw, aby zwrócenie uwagi na problem, ale gdy poprawka zostanie wkompilowana w kod wywołujący, nie musisz się już martwić o jej sprawdzenie. W czasie kompilacji nie można rozwiązać problemów, takich jak brak pamięci lub złe dane wejściowe użytkownika końcowego, więc pozostaw kontrolę.

Karl Bielefeldt
źródło
2

Sprawdź wszystko w dowolnym momencie (mogło się to zmienić po ostatniej kontroli), co nie jest w 100% pod twoim dowództwem. A także: Podczas rozwoju nawet nie ufaj sobie! ;-)

Okokok ... „cokolwiek” należy rozumieć jako sprawdzanie takich rzeczy, które spowodowałyby nienormalne przerwanie lub cokolwiek, co mogłoby zmusić twoją aplikację / system do robienia rzeczy, których nie powinno robić.

Mówiąc poważnie, ostatnia część ostatniego zdania jest niezbędna, ponieważ wskazuje na główny problem:

Jeśli chcesz zbudować stabilny system główny problem to nie o tym, co system powinien robić, ale niech to będzie w stanie zrobić takich obowiązkowych rzeczy, trzeba zadbać o to, co powinno nie robić, nawet jeśli jest „zaśmiecanie kod".

alk
źródło
1
+1 za „sprawdź wszystko”. Nie kupuję argumentu bałaganu kodu: każdy programista powinien być w stanie odróżnić sprawdzanie błędów od rzeczywistej logiki.
stijn
2

Istotą obsługi błędów nie jest to, czy i jak złapiesz problem. To więcej, co robisz po tym, jak się o tym dowiesz .

Po pierwsze - powiedziałbym, że nie ma powodu, dla którego zwracany byłby pojedynczy błąd zwracany metodą podrzędną. Błędy i wyjątki to coś więcej niż zwracane wartości lub wszystkie try / catch.

  1. Sam rzut i łapanie nie wystarczy.
    Zobacz : gdzie autor wyjaśnia, że ​​po prostu wyłapując, ale nie robiąc nic, potencjalnie nie tłumi wyjątku i jeśli nie zrobi się wystarczająco dużo, aby cofnąć uszkodzenie - jest to gorsze niż pozostawienie kodu w ten sposób. Podobnie samo napisanie instrukcji „log”, gdy plik jest otwarty lub błąd odczytu, może pomóc w znalezieniu przyczyny - ale z upływem czasu program może spowodować uszkodzenie danych! Nie wystarczy powiedzieć, że mam wiele prób połowu - ważniejsze jest, aby wiedzieć, co naprawdę robią!

  2. Nie nadużywaj próby złapania.
    Czasami - głównie leniwi lub naiwni programiści myślą, że po napisaniu wystarczającej liczby prób / łapań ich praca jest skończona i łatwa. Dość często najlepiej jest zastosować działanie naprawcze i wznowić, niż po prostu zrzucić całą rzecz. Jeśli nie można tego zrobić, należy zdecydować, na jakim poziomie należy wrócić. W zależności od kontekstu i wagi, spróbuj zagnieżdżenia wymaga dokładnego zaprojektowania. Na przykład - Zobacz to i to

  3. Określ, kto jest odpowiedzialny:
    Jedną z pierwszych rzeczy, które powinieneś zrobić, jest określenie, czy dane wejściowe przekazane do samej procedury są po prostu niedopuszczalnym (lub jak dotąd nieobsługiwanym) scenariuszem, czy też stanowią wyjątek ze względu na środowisko (takie jak problem z systemem, problem z pamięcią) lub czy jest to sytuacja całkowicie wewnętrzna wynikająca z wyniku algorytmu. We wszystkich przypadkach - poziom, do którego możesz wrócić lub działanie, które chcesz podjąć, różni się znacznie. W tym świetle chciałbym powiedzieć - że kiedy uruchomisz kod w środowisku produkcyjnym - wykonanie abort () do wyjścia z programu jest dobre - ale nie dla każdej drobnej rzeczy. Jeśli zauważysz uszkodzenie pamięci lub brak pamięci, to z całą pewnością nawet po zrobieniu wszystkiego, co najlepsze, wszystko umrze. Ale jeśli na wejściu pojawi się wskaźnik NULL - nie zrobiłbym tego

  4. Zdefiniuj, co jest najlepszym możliwym rezultatem: to,
    co wszystkie rzeczy muszą być zrobione z wyjątkiem, jest bardzo ważne. Na przykład, jeśli w jednym z naszych przypadków - odtwarzacz multimediów stwierdzi, że nie ma pełnych danych, które można odtworzyć dla użytkownika - co należy zrobić?

    • albo pomiń jakąś złą stronę i sprawdź, czy da radę zrobić coś dobrego.
    • jeśli tak się stanie, zastanów się, czy można przejść do następnej piosenki.
    • jeśli stwierdzi, że nie jest w stanie odczytać żadnego pliku - zatrzymaj i pokaż coś.
    • w międzyczasie
    • pod którym stanem powinien zostać wyświetlony POP-UP gracz i
    • kiedy powinien działać samodzielnie?
    • Powinno to „zatrzymać” rzeczy, by poprosić użytkownika o informacje zwrotne
    • czy powinien umieścić dyskretną notatkę o błędzie w rogu?

    Wszystko to jest subiektywne - i być może istnieje więcej sposobów radzenia sobie z problemami niż trywialnie. Wszystkie powyższe wymagają zbudowania i zrozumienia głębi wyjątku, a także umożliwienia ewolucji różnych scenariuszy.

  5. Czasami musimy sprawdzić wyjątki, zanim się pojawią. Najczęstszym przykładem jest błąd dzielenia przez zero. Najlepiej jest przetestować to, zanim zostanie narzucony taki wyjątek - a jeśli tak jest - spróbuj wprowadzić najbardziej odpowiednią wartość niezerową i przejść dalej, niż popaść w samobójstwo!

  6. Sprzątać. Przynajmniej to musisz zrobić! Jeśli funkcja otworzy 3 pliki, a czwarta nie otworzy się - nie trzeba dodawać, że pierwsze 3 powinny zostać zamknięte. Delegowanie tej pracy do powyższej warstwy to zły pomysł. jeśli zdecydujesz się nie wychodzić bez czyszczenia pamięci. I najważniejsze - nawet jeśli przetrwałeś wyjątek, informuj wyżej, że sprawy nie potoczyły się normalnie.

Sposób, w jaki widzimy (normalną) funkcjonalność oprogramowania w kategoriach różnych hierarchii, warstw lub abstrakcji, w ten sam sposób musimy kategoryzować wyjątki na podstawie ich ważności, a także zakresu, w jakim powstają i wpływają na inne części systemu - które definiują jak radzić sobie z takimi wyjątkami w najlepszy możliwy sposób.

Najlepsze źródło: Code Craft rozdział 6 - dostępny do pobrania

Dipan Mehta
źródło
1

Sprawdzanie błędów tylko podczas kompilacji debugowania to ZŁA POMYSŁ (tm), kompilacja w ramach nakładek nakładanych zmiennych wielokrotnego użytku na stosie, usuwa strony ochronne, robi niepewne sztuczki z obliczeniami, zastępuje ciężkie artretyki wstępnie obliczonymi przesunięciami i tak dalej.

Skorzystaj również ze sprawdzania błędów w wydaniu, możesz skorzystać z czegoś tak prostego, jak:

if(somethinghitthefan)
     abort();

Ma to również bardzo dobry efekt uboczny, na pewno nie zignorujesz błędu, gdy aplikacja zacznie się zawieszać na betta testers PC.

Przeglądarki zdarzeń i dzienniki są całkowicie bezużyteczne w porównaniu do tego abort(), kto i tak je sprawdza?

Koder
źródło
exit / abort == najgorsze doświadczenie użytkownika: aplikacja po prostu znika bez podania przyczyny ..
stijn
@stijn: abortwłamuje się do debuggera / tworzy zrzut. exitjest zły, tak. Chociaż __asm int 3najbardziej wolę .
Koder
to prawda, a w C / C ++ zwykle piszę twierdzenia używając __asm ​​int 3, ale nigdy nie pokazując przynajmniej opisu dlaczego, a najlepiej także wiersza i pliku. Wtedy przynajmniej klient może podać informacje o tym, co dokładnie się wydarzyło.
stijn
0

Różne rzeczy, które możesz zrobić to: 1.
Przeczytać i przyswoić dużo kodu online i zobaczyć, jak to jest zrobione
2. Użyj niektórych narzędzi do debugowania, aby pomóc Ci zlokalizować regiony błędów
3. Bądź świadomy trywialnych błędów z powodu niewłaściwych przypisań i błędy składniowe.
4. Niektóre gorsze błędy powstają z powodu błędów logicznych w programie, które są trudniejsze do znalezienia. W tym celu możesz je wypisać i znaleźć, lub w przypadku bardziej skomplikowanych spróbuj porozmawiać z ludźmi lub skorzystać z zasobów takich jak Stackoverflow , Wikipedia , Google, aby uzyskać pomoc od ludzie.

DCV
źródło