Niedawno natknąłem się na platformę Microsoft dotyczącą kontraktów kodowych.
Przeczytałem trochę dokumentacji i ciągle pytałem: „Dlaczego miałbym kiedykolwiek chcieć to robić, ponieważ nie wykonuje i często nie może przeprowadzić analizy statycznej”.
Teraz mam już pewien rodzaj defensywnego stylu programowania, z takimi wyjątkami:
if(var == null) { throw new NullArgumentException(); }
Używam również dużo NullObject Pattern i rzadko mam problemy. Dodaj do niego testy jednostkowe i wszystko gotowe.
Nigdy nie użyłem twierdzeń i nigdy ich nie przegapiłem. Wręcz przeciwnie. Naprawdę nienawidzę kodu, który ma wiele bezsensownych zapewnień, co jest dla mnie tylko hałasem i odciąga mnie od tego, co naprawdę chcę zobaczyć. Kontrakty kodowe, przynajmniej sposób Microsoftu, są prawie takie same - a nawet gorzej. Dodają dużo szumu i złożoności do kodu. W 99% i tak zostanie zgłoszony wyjątek - więc nie dbam o to, czy wynika to z twierdzenia / kontraktu, czy z faktycznego problemu. Pozostaje tylko bardzo niewiele przypadków, w których stany programu naprawdę ulegają uszkodzeniu.
Szczerze mówiąc, jaka jest korzyść ze stosowania umów kodowych? Czy jest w ogóle coś takiego? Jeśli używasz już testów jednostkowych i kodu w obronie, uważam, że wprowadzenie umów nie jest warte kosztów i powoduje hałas w kodzie, który opiekun przeklnie, gdy aktualizuje tę metodę, podobnie jak ja, gdy nie widzę, co robi kod z powodu bezużytecznych twierdzeń. Nie widziałem jeszcze dobrego powodu, aby zapłacić tę cenę.
źródło
Odpowiedzi:
Równie dobrze możesz zapytać, kiedy pisanie statyczne jest lepsze niż pisanie dynamiczne. Debata szalona od lat bez końca. Spójrz więc na pytania takie jak dynamiczne i statyczne badania języków
Ale podstawowym argumentem byłby potencjał wykrycia i rozwiązania problemów w czasie kompilacji, które w przeciwnym razie mogłyby trafić do produkcji.
Kontrakty kontra Strażnicy
Nawet ze strażnikami i wyjątkami nadal masz problem z tym, że system nie wykona zamierzonego zadania w niektórych przypadkach. Możliwe, że wystąpienie, które będzie bardzo krytyczne i kosztowne, nie powiedzie się.
Kontrakty a testy jednostkowe
Typowym argumentem w tym przypadku jest to, że testy dowodzą obecności błędów, podczas gdy typy (kontrakty) dowodzą braku. F.ex. używając typu, który wiesz, że żadna ścieżka w programie prawdopodobnie nie może dostarczyć nieprawidłowych danych wejściowych, podczas gdy test może jedynie stwierdzić, że ścieżka objęta zawierała prawidłowe dane wejściowe.
Kontrakty a wzorzec obiektu zerowego
Teraz jest to przynajmniej w tym samym parku. Języki takie jak Scala i Haskell odniosły wielki sukces dzięki temu podejściu do całkowitego wyeliminowania zerowych referencji z programów. (Nawet jeśli Scala formalnie dopuszcza wartości zerowe, konwencja nigdy ich nie używa)
Jeśli zastosujesz już ten wzorzec do wyeliminowania NRE, w zasadzie usunąłeś największe źródło awarii środowiska uruchomieniowego, w zasadzie sposób pozwala na to umowy.
Różnica może być taka, że kontrakty mają opcję automatycznego żądania całego kodu, aby uniknąć wartości zerowej, a tym samym zmusić cię do korzystania z tego wzorca w większej liczbie miejsc do przekazania kompilacji.
Oprócz tego umowy zapewniają również elastyczność w kierowaniu na cele wykraczające poza zero. Więc jeśli nie widzisz już NRE w swoich błędach, możesz użyć kontraktów, aby zdławić następny najczęstszy problem, jaki możesz mieć. Wyłączone przez jednego? Indeks poza zakresem?
Ale...
Wszystko to powiedziane. Zgadzam się, że umowy dotyczące szumu syntaktycznego (a nawet szumu strukturalnego) zwiększające kod są dość znaczące i nie należy lekceważyć wpływu analizy na czas kompilacji. Więc jeśli zdecydujesz się dodać kontrakty do swojego systemu, rozsądnie byłoby zrobić to bardzo ostrożnie, z wąskim naciskiem na to, którą klasę błędów próbuje się rozwiązać.
źródło
Nie wiem, skąd pochodzi twierdzenie, że „nie wykonuje i często nie może przeprowadzić analizy statycznej”. Pierwsza część tego twierdzenia jest po prostu błędna. Drugi zależy od tego, co rozumiesz przez „często”. Wolałbym powiedzieć, że często wykonuje analizę statyczną, a rzadko zawodzi. W zwykłej aplikacji biznesowej rzadko staje się o wiele bliżej nigdy .
Oto pierwsza korzyść:
Korzyści 1: analiza statyczna
Zwykłe twierdzenia i sprawdzanie argumentów mają wadę: są one odraczane do momentu wykonania kodu. Z drugiej strony, umowy kodowe przejawiają się na znacznie wcześniejszym poziomie, albo na etapie kodowania, albo podczas kompilacji aplikacji. Im wcześniej błąd zostanie wykryty, tym taniej go naprawić.
Korzyści 2: rodzaj zawsze aktualnej dokumentacji
Umowy kodeksowe dostarczają również pewnego rodzaju dokumentacji, która jest zawsze aktualna. Jeśli komentarz XML tej metody
SetProductPrice(int newPrice)
mówi, żenewPrice
powinna być lepsza lub równa zero, możesz mieć nadzieję, że dokumentacja jest aktualna, ale możesz również odkryć, że ktoś zmienił metodę, abynewPrice = 0
rzucićArgumentOutOfRangeException
, ale nigdy nie zmieniła odpowiedniej dokumentacji. Biorąc pod uwagę korelację między umowami kodu a samym kodem, nie masz problemu z brakiem synchronizacji dokumentacji.Dokumentacja dostarczana przez kontrakty kodowe jest również cenna, ponieważ często komentarze XML nie wyjaśniają dobrze dopuszczalnych wartości. Ile razy ja zastanawiałem się, czy
null
lubstring.Empty
czy\r\n
jest autoryzowanym wartość dla metody i komentarze XML milczeli na ten temat!Podsumowując, bez umów kodowych wiele fragmentów kodu wygląda tak:
Dzięki umowom kodowym staje się:
Korzyści 3: umowy dotyczące interfejsów
Trzecią korzyścią jest to, że umowy kodowe wzmacniają interfejsy. Powiedzmy, że masz coś takiego:
W jaki sposób, korzystając wyłącznie z twierdzeń i wyjątków, gwarantujesz, że
CommitChanges
można je wywołać tylko wtedy, gdyPendingChanges
nie jest puste? Jak byś zagwarantował, żePendingChanges
nigdy tak nie jestnull
?Korzyści 4: egzekwowanie wyników metody
Wreszcie czwartą korzyścią jest możliwość
Contract.Ensure
uzyskania wyników. Co jeśli, pisząc metodę zwracającą liczbę całkowitą, chcę mieć pewność, że wartość nigdy nie jest gorsza ani równa zero? W tym pięć lat później, po wielu zmianach od wielu programistów? Gdy tylko metoda ma wiele punktów powrotu,Assert
staje się koszmarem konserwacyjnym.Rozważ umowy o kod nie tylko jako sposób na poprawność kodu, ale także na bardziej rygorystyczny sposób pisania kodu. W podobny sposób osoba, która używa wyłącznie języków dynamicznych, może zapytać, dlaczego wymuszać typy na poziomie językowym, podczas gdy możesz zrobić to samo w asercjach, gdy jest to potrzebne. Możesz, ale pisanie statyczne jest łatwiejsze w użyciu, mniej podatne na błędy w porównaniu do szeregu twierdzeń i samo dokumentowania.
Różnica między pisaniem dynamicznym a pisaniem statycznym jest bardzo zbliżona do różnicy między zwykłym programowaniem a programowaniem na podstawie umów.
źródło
Wiem, że to trochę za późno na pytanie, ale jest jeszcze jedna korzyść z Code Contracts, o której należy wspomnieć
Kontrakty są sprawdzane poza metodą
Kiedy w naszej metodzie mamy warunki ochrony, jest to miejsce, w którym odbywa się sprawdzanie i gdzie zgłaszane są błędy. Być może będziemy musieli zbadać ślad stosu, aby dowiedzieć się, gdzie jest rzeczywiste źródło błędu.
Kontrakty kodu znajdują się w obrębie metody, ale warunki wstępne są sprawdzane poza metodą, gdy cokolwiek próbuje wywołać tę metodę. Kontrakty stają się częścią metody i określają, czy można ją wywołać, czy nie. Oznacza to, że otrzymujemy zgłoszony błąd znacznie bliżej faktycznego źródła problemu.
Jeśli masz metodę chronioną umową, która jest wymagana dla wielu miejsc, może to być realna korzyść.
źródło
Dwie rzeczy naprawdę:
źródło