ale zawieszanie oprogramowania klienta nadal nie jest dobrą rzeczą
Z pewnością jest to dobra rzecz.
Chcesz, aby wszystko, co pozostawia system w nieokreślonym stanie, zatrzymało system, ponieważ niezdefiniowany system może robić okropne rzeczy, takie jak uszkodzone dane, formatować dysk twardy i wysyłać wiadomości e-mail do prezydenta. Jeśli nie możesz odzyskać systemu i przywrócić go do określonego stanu, przyczyną awarii jest awaria. Właśnie dlatego budujemy systemy, które powodują awarie, a nie cicho się rozrywają. Teraz pewnie wszyscy chcemy stabilnego systemu, który nigdy się nie zawiesza, ale naprawdę chcemy tego tylko wtedy, gdy system pozostaje w zdefiniowanym przewidywalnym bezpiecznym stanie.
Słyszałem, że wyjątki w zakresie kontroli są uważane za poważny antypattern
To absolutnie prawda, ale często jest niezrozumiana. Kiedy wymyślili system wyjątków, bali się, że łamią programowanie strukturalne. Programowanie strukturalne dlatego mamy for
, while
, until
, break
, i continue
gdy wszystko, czego potrzebujemy, aby zrobić wszystko, że jest goto
.
Dijkstra nauczył nas, że używanie goto w sposób nieformalny (czyli skakanie w dowolne miejsce) sprawia, że czytanie kodu jest koszmarem. Kiedy dali nam system wyjątków, bali się, że wymyślają na nowo goto. Dlatego powiedzieli nam, aby nie „używać go do kontroli przepływu”, mając nadzieję, że zrozumiemy. Niestety wielu z nas tego nie zrobiło.
O dziwo, często nie nadużywamy wyjątków do tworzenia kodu spaghetti, jak to było w przypadku goto. Wydaje się, że sama rada spowodowała więcej problemów.
Zasadniczo wyjątki dotyczą odrzucenia założenia. Gdy poprosisz o zapisanie pliku, zakładasz, że plik może i zostanie zapisany. Wyjątek występuje, gdy nie może być to niezgodne z prawem, że HD jest pełny lub ponieważ szczur wgryzł się w kabel danych. Możesz obsłużyć wszystkie te błędy w różny sposób, możesz obsłużyć je wszystkie w ten sam sposób lub możesz pozwolić im zatrzymać system. W twoim kodzie jest szczęśliwa ścieżka, w której twoje założenia muszą być prawdziwe. Tak czy inaczej wyjątki zabierają cię z tej szczęśliwej ścieżki. Ściśle mówiąc, tak, to rodzaj „kontroli przepływu”, ale nie o to ci ostrzegali. Mówili o takich bzdurach :
„Wyjątki powinny być wyjątkowe”. Ta mała tautologia narodziła się, ponieważ projektanci systemów wyjątków potrzebują czasu na tworzenie śladów stosu. W porównaniu do skakania jest to powolne. Zjada czas procesora. Ale jeśli masz zamiar zalogować się i zatrzymać system lub przynajmniej zatrzymać bieżące intensywne przetwarzanie przed uruchomieniem następnego, masz trochę czasu na zabicie. Jeśli ludzie zaczną używać wyjątków „do kontroli przepływu”, te założenia dotyczące czasu znikną z okna. Tak więc „wyjątki powinny być wyjątkowe” zostały nam przekazane jako wzgląd na wydajność.
Znacznie ważniejsze niż to nas nie myli. Jak długo zajęło ci zauważenie nieskończonej pętli w powyższym kodzie?
NIE zwracaj kodów błędów.
... to dobra rada, gdy jesteś w bazie kodu, która zwykle nie używa kodów błędów. Dlaczego? Ponieważ nikt nie będzie pamiętać, aby zapisać wartość zwracaną i sprawdzić kody błędów. To wciąż dobra konwencja, gdy jesteś w C.
OneOf
Używasz innej konwencji. W porządku, o ile ustanawiasz konwencję, a nie tylko walczysz z inną. Mylące są dwie konwencje błędów w tej samej bazie kodu. Jeśli w jakiś sposób pozbyłeś się całego kodu korzystającego z innej konwencji, to idź dalej.
Sam lubię konwencję. Jedno z najlepszych wyjaśnień, jakie tu znalazłem * :
Ale bardzo mi się podoba i nadal nie będę mieszał go z innymi konwencjami. Wybierz jeden i trzymaj się go. 1
1: Rozumiem przez to, że nie myślę o więcej niż jednej konwencji w tym samym czasie.
Kolejne przemyślenia:
Z tej dyskusji obecnie zabieram następujące rzeczy:
- Jeśli oczekujesz, że bezpośredni rozmówca będzie przechwytywał wyjątek przez większość czasu i kontynuował pracę, być może inną ścieżką, prawdopodobnie powinien być częścią typu zwracanego. Opcjonalne lub OneOf mogą być przydatne tutaj.
- Jeśli oczekujesz, że bezpośredni dzwoniący nie będzie wychwytywał wyjątku przez większość czasu, wyrzuć wyjątek, aby zaoszczędzić głupoty ręcznego przekazywania go na stos.
- Jeśli nie masz pewności, co zrobi bezpośredni rozmówca, być może podaj oba, takie jak Parse i TryParse.
To naprawdę nie jest takie proste. Jedną z podstawowych rzeczy, które musisz zrozumieć, jest zero.
Ile dni pozostało w maju? 0 (ponieważ to nie jest maj. To już czerwiec).
Wyjątki są sposobem na odrzucenie założenia, ale nie są jedynym sposobem. Jeśli użyjesz wyjątków, aby odrzucić założenie, opuścisz szczęśliwą ścieżkę. Ale jeśli wybrałeś wartości, aby zesłać szczęśliwą ścieżkę, która sygnalizuje, że rzeczy nie są tak proste, jak zakładano, możesz pozostać na tej ścieżce, dopóki będzie ona w stanie poradzić sobie z tymi wartościami. Czasami 0 jest już używane, aby coś oznaczać, więc musisz znaleźć inną wartość, na którą odwzorujesz swój pomysł odrzucenia założenia. Możesz rozpoznać ten pomysł po jego użyciu w starej dobrej algebrze . Monady mogą w tym pomóc, ale nie zawsze musi to być monada.
Na przykład 2 :
IList<int> ParseAllTheInts(String s) { ... }
Czy potrafisz wymyślić jakiś dobry powód, dla którego musi to być zaprojektowane tak, aby celowo rzucało cokolwiek? Zgadnij, co otrzymasz, gdy nie można przeanalizować żadnego elementu int? Nawet nie muszę ci mówić.
To znak dobrego imienia. Przepraszam, ale TryParse nie jest moim pomysłem na dobre imię.
Często unikamy rzucania wyjątku na nieotrzymanie niczego, gdy odpowiedź może być więcej niż jedną rzeczą w tym samym czasie, ale z jakiegoś powodu, jeśli odpowiedź jest albo jedna, albo niczym, mamy obsesję na punkcie nalegania, aby dać nam jedną rzecz lub rzucić:
IList<Point> Intersection(Line a, Line b) { ... }
Czy linie równoległe naprawdę muszą powodować tutaj wyjątek? Czy to naprawdę takie złe, jeśli ta lista nigdy nie będzie zawierać więcej niż jednego punktu?
Może semantycznie po prostu nie możesz tego znieść. Jeśli tak, szkoda. Ale może Monady, które nie mają tak dowolnych rozmiarów, jak List
to, sprawią, że poczujesz się lepiej.
Maybe<Point> Intersection(Line a, Line b) { ... }
Monady to małe kolekcje specjalnego przeznaczenia, które mają być używane w określony sposób, dzięki czemu nie trzeba ich testować. Powinniśmy znaleźć sposoby radzenia sobie z nimi bez względu na to, co zawierają. W ten sposób szczęśliwa ścieżka pozostaje prosta. Jeśli otworzysz się i przetestujesz każdą dotkniętą Monadę, używasz ich źle.
Wiem, to dziwne. Ale to nowe narzędzie (cóż, dla nas). Daj więc trochę czasu. Młotki mają większy sens, gdy przestajesz używać ich na śrubach.
Jeśli mi pozwolisz, chciałbym odpowiedzieć na ten komentarz:
Dlaczego żadna z odpowiedzi nie wyjaśnia, że Albo monada nie jest kodem błędu, ani OneOf? Różnią się one zasadniczo, a zatem wydaje się, że pytanie opiera się na nieporozumieniu. (Chociaż w zmodyfikowanej formie jest to wciąż ważne pytanie.) - Konrad Rudolph 4 czerwca 18 o 14:08
To jest absolutna prawda. Monady są znacznie bliżej kolekcji niż wyjątki, flagi lub kody błędów. Robią dobre pojemniki na takie rzeczy, jeśli są mądrze używane.
Result
;-)