Jak mogę zdefiniować i zmierzyć prostotę w kodzie?

13

Istnieje wiele odpowiedzi w moim poprzednim pytaniu na temat prostoty związanej z czytelnością, która pomogła mi zobaczyć moją definicję, a zrozumienie prostoty w kodzie było, być może, niepoprawne.

Jak zdefiniować prostotę w kodzie? Jakie pomiary i metryki oprogramowania są dostępne do pomiaru prostoty kodu?

Richard
źródło
2
@MarkTrapp Istnieją inne sposoby omawiania prostoty kodu bez tematów z empirycznej inżynierii oprogramowania, tematów, z którymi jestem znacznie mniej zaznajomiony. Na przykład omawianie prostoty w zakresie umiejętności pisania automatycznych testów. Moje umiejętności i wiedza pozwalają mi odpowiedzieć na to pytanie z perspektywy inżyniera oprogramowania, podczas gdy inni mogą odpowiedzieć z innej perspektywy. Dodanie tego stwierdzenia do pytania znacznie ogranicza liczbę przydatnych odpowiedzi, przez co jest zbyt zlokalizowane (IMO). Jeśli chcesz go dodać, możesz, ale to dobre pytanie.
Thomas Owens
2
@ThomasOwens Prawdziwe pytania mają odpowiedzi , a nie pomysły ani opinie. Zawężenie zakresu, aby wszyscy interpretowali, jak odpowiedzieć na pytanie w ten sam sposób, jest dokładnie tym, o co chodzi w Stack Exchange. Może istnieć więcej niż jedno podejście do rozwiązania problemu, ale jest tylko jeden jednoznacznie określony problem.
W obecnym stanie odpowiedzi na to pytanie jest bardzo niewiele (moja odpowiedź odnosi się do empirycznego punktu widzenia inżynierii oprogramowania wraz ze wspólnymi miernikami - prawdopodobnie są inne). Nie ma sensu wykluczanie odpowiedzi zapewniających prawidłowe alternatywy z innych perspektyw, co właśnie brzmi sformułowanie tego pytania. Nie zgadzam się całkowicie z tymi zmianami i pytanie powinno zostać przywrócone do pierwotnej formy.
Thomas Owens
@MarkTrapp Problem jest jednoznaczny: jak określić prostotę kodu? Istnieje kilka dobrych odpowiedzi. Mój używa empirycznych technik inżynierii oprogramowania do pomiaru złożoności. Innym może być pisanie testów automatycznych, a jeśli trudno jest napisać dobre testy, kod jest złożony - odpowiedź jest całkowicie poprawna. Mogą istnieć inne, o których nie wiem. Jeśli potrzebujesz zmierzyć złożoność / prostotę podstawy kodu, pytanie powinno być sformułowane w sposób umożliwiający przedstawienie wszystkich alternatyw, aby pytający mógł wybrać najlepsze rozwiązanie dla swojej konkretnej sprawy.
Thomas Owens

Odpowiedzi:

16

Najczęstsze miary do pomiaru złożoności (lub prostoty, jeśli uważasz, że prostota jest przeciwieństwem złożoności) to Cyklomatyczna złożoność McCabe i Metryka złożoności Halsteada .

Cyklomatyczna złożoność mierzy liczbę różnych ścieżek przez daną jednostkę, zwykle metodę lub funkcję, chociaż można ją również obliczyć dla klasy. Wraz ze wzrostem liczby ścieżek trudniej jest zapamiętać przepływ danych przez dany moduł, co wiąże się z koncepcją pamięci roboczej . Wysoka złożoność cykliczna wskazuje na trudności w testowaniu modułu - potrzeba więcej przypadków testowych, aby pokryć różne ścieżki w systemie. Istnieją również badania, które połączyły wysoką złożoność cyklomatyczną z wysokimi wskaźnikami wad. Zazwyczaj cykliczna złożoność wynosząca 10 wskazuje, że jednostka powinna zostać poddana przeglądowi i być może ponownie przetworzona.

Miary złożoności Halsteada wykorzystują dane wejściowe całkowitych i odrębnych operatorów i operandów do obliczenia objętości, trudności i wysiłku fragmentu kodu. Trudność, którą jest (liczba unikalnych operatorów / 2) * (całkowita liczba argumentów / liczba unikalnych argumentów), wiąże się ze zdolnością do czytania i rozumienia kodu dla zadań takich jak nauka systemu lub przegląd kodu. Ponownie można to policzyć na poziomie systemu, klasy lub metody / funkcji. Jest kilka postów na temat obliczania tych pomiarów tutaj i tutaj .

Po prostu zliczanie linii kodu może również dać ci wyobrażenie o złożoności. Więcej wierszy kodu oznacza, że ​​w module jest więcej do odczytania i zrozumienia. Wahałbym się, czy wykorzystać to jako samodzielny pomiar. Zamiast tego użyłbym go do innych pomiarów, takich jak liczba defektów w danym module, aby uzyskać gęstość defektów. Wysoka gęstość defektów może wskazywać na problemy z pisaniem testów i przeglądaniem kodu, które mogą, ale nie muszą być spowodowane złożonym kodem.

Wachlowanie i wachlowanie to dwa inne wskaźniki związane z przepływem danych. Jak zdefiniowano tutaj , wejście wentylatora to suma wywoływanych procedur, odczyt parametrów, a odczyt zmiennych globalnych i wyciągnięcie wentylatora to suma procedur, które wywołują daną procedurę, parametry zapisywane (narażone na użytkowanie przez użytkowników zewnętrznych, przekazane przez referencję), i zmienne globalne zapisane do. Ponownie, wysokie wejście i wyjście może wskazywać na moduł, który może być trudny do zrozumienia.

W określonych paradygmatach mogą być przydatne inne miary lub wskaźniki. Na przykład w świecie zorientowanym obiektowo można zastosować monitorowanie sprzężenia (pożądane niskie), kohezji (pożądane wysokie) i głębokości dziedziczenia (pożądane niskie), aby ocenić, jak prosty lub skomplikowany jest system.

Oczywiście ważne jest, aby zdawać sobie sprawę, że wiele miar i wskaźników to po prostu wskaźniki. Musisz użyć swojej oceny, aby ustalić, czy konieczne jest dokonanie refaktoryzacji w celu zwiększenia prostoty, czy też nie warto tego robić. Możesz dokonywać pomiarów, obliczać metryki i poznawać kod, ale nie chcesz projektować systemu na podstawie liczb. Ostatecznie rób to, co ma sens.

Thomas Owens
źródło
5
Wiem, że o tym wspomniałeś, ale ważne jest, aby podkreślić, że złożoność cykliczna jest naprawdę przydatna tylko na poziomie funkcji / metody i staje się znacznie bardziej subiektywna / bezużyteczna na wyższych poziomach.
Ryathal,
Problem polega na tym, że chociaż te środki są dobre, ogólny przewodnik. Istnieje kilka przypadków, w których „złe” programy osiągają dobre wyniki, na przykład posiadanie kilkunastu funkcji, w których wystarczyłaby jedna funkcja z dwoma dodatkowymi parametrami, i odwrotnie, wiele „dobrych” programów, które są dobrze napisanymi rozwiązaniami złożonego problemu, mogą uzyskać ocenę źle.
James Anderson,
@James wyraźnie to podkreśliłem. Wszelkie pomiary lub miary należy traktować w kontekście jako wskaźnik, na który należy zwrócić uwagę. Inżynier musi osądzić, czy potrzebne jest działanie naprawcze i jakie to działanie. Jednak, chyba że aktywnie gromadzisz dane, nie ma empirycznego sposobu na poznanie potencjalnych problemów.
Thomas Owens
7

Zamiast patrzeć na formalny tryb definiowania prostoty, wolałbym zdefiniować prostotę jako atrybut jakości pisania kodu.

Nie stawiam pewnej prostoty, ale kiedy nazywasz coś prostym, czy nie.

1. Przejście kodu:
jak łatwo poruszać się po kodzie? Czy łatwo jest zauważyć, gdzie są zapisane funkcje API? Czy łatwo jest zrozumieć przepływy wywołań, na przykład, które metody wywołują inne (i dlaczego) - czy są zaimplementowane dobre maszyny stanów lub algorytmy jednoznacznie określone?

Gdy przejście do kodu jest łatwe, kod jest prosty do naśladowania.

2. Nazewnictwo
Podczas gdy inne standardy kodowania sprawiają, że kod wygląda na bardziej przejrzysty - najważniejsze jest nazywanie klas / instancji obiektów / Zmiennych / metod. Stosowanie jasnych i jednoznacznych nazw jest wyraźnie ma ogromny wpływ na Prostota kodu. Kiedy trudno jest zidentyfikować prostą nazwę, jest to znak, że możesz chcieć ponownie przemyśleć pomysł, że jest to zmienna / metoda.

3. Interpretacja i odniesienia
Czy każda z metod ma do odegrania wyraźną rolę. Czy każdą zmienną / atrybut można łatwo określić, jaką rolę odgrywają? Gdy fragment kodu robi coś, co implikuje założenia lub wpływa na niepowiązany zestaw zmiennych, może stać się koszmarem konserwacyjnym.

4. Zależność lub sprzężenie
Trudno to ocenić na podstawie samego kodu, ale staje się bardzo widoczne, jeśli ktoś próbuje naprawić twoje błędy. Kiedy jakieś inne rzeczy zmieniają się w innym obiekcie, czy operacja tutaj się zmienia? Czy te zmiany są oczywiste? Czy musisz tak często zmieniać interfejs API, aby uwzględnić różne rzeczy. Sugerują one, że relacje między modułami nie są proste

5. Dane wejściowe użytkownika lub aplikacji
Wreszcie, jak proste są dane wejściowe użytkownika lub aplikacja są akceptowane w interfejsie API / UI? Kiedy wielu możliwych użytkowników / aplikacji (do różnych celów) musi ci dać - czy są oczywiste? Czy istnieją stany / szczegóły, które nie są związane z wyższą abstrakcją, ale wciąż przechodzą do przodu i do tyłu?

Proste pytanie, które zazwyczaj zadałbym, brzmi następująco: Jeśli zamiast programu, gdybym poprosił człowieka o tę samą funkcję, czy wypełniłbym tę informację w formie papierowej ? Jeśli nie, nie jestem tutaj wystarczająco prosty .

Nie powiem, że ta lista jest wyczerpująca, ale sądzę, że kryteria są takie, jak łatwe lub trudne jest używanie i modyfikowanie oprogramowania. To jest proste.

Dipan Mehta
źródło
1
Poprosił o pomiary i metryki. Są subiektywne, więc nie sądzę, żeby były bardzo trafne.
psr
@psr Zgadzam się z tobą. Było to również bardzo widoczne z odpowiedzi Thomasa. Wspomniał jednak o prostocie związanej z czytelnością . Odpowiedź Tomasza dotyczy złożoności cyklomatycznej - informuje o tym, jak skomplikowane jest testowanie kodu, a nie o tym, jak skomplikowany jest kod pod względem czytelności i może rozszerzyć konserwowalność . Są to dwie bardzo różne koncepcje. Właśnie dlatego napisałem tę odpowiedź, aby postawić wyraźną sprzeczność. Niestety, o ile mi wiadomo, nie ma wskaźników odnoszących się do prostoty kodu pod względem czytelności.
Dipan Mehta,
„używaj nazw, których nie można źle zrozumieć” - IMHO celuje zbyt wysoko, do nierealistycznego i niemożliwego celu. Wolałbym nie być tak zdecydowanym i po prostu powiedzieć „używaj jasnych i jednoznacznych nazw”.
Péter Török,
@ PéterTörök Zgadzam się. Myślę, że zwykle w wielu organizacjach bardzo jasno zdefiniowane zasady konwencji nazewnictwa i nadal istnieje pewne zamieszanie co do intencji konkretnej zmiennej. Dlatego podkreślono, że jasność celu sprowadza się do prostoty, a nie do formalnej reguły. Być może poszedłem za burtę w opisie. Dzięki.
Dipan Mehta
Złożoność @Dipan Cyclomatic związana jest z czytelnością kodu poprzez pamięć roboczą. Kod o dużej złożoności cyklicznej (lub nawet po prostu dużej głębokości bloku) jest trudny do utrzymania w pamięci roboczej, dlatego jest trudniejszy do odczytania od razu.
Thomas Owens
0

Nie znam żadnych istniejących dobrych wskaźników dotyczących prostoty kodu (nie oznacza to, że nie istnieją - po prostu nie wiem o nich). Mógłbym zaproponować niektóre, może niektóre pomogą:

  • Prostota zastosowanych funkcji językowych: jeśli język ma funkcje, które można uznać za „zaawansowane” i „proste”, można policzyć liczbę wystąpień funkcji zaawansowanych. Sposób definiowania „zaawansowany” może być nieco bardziej subiektywny. Podejrzewam, że niektórzy mogliby powiedzieć, że jest to również pomiar „sprytności” programu. Typowy przykład: niektórzy mogą powiedzieć, że ?:operator powinien być funkcją „zaawansowaną”, inni mogą się nie zgodzić. Nie wiem, jak łatwo byłoby napisać narzędzie, które może to przetestować.

  • Prostota konstrukcji w programie: Możesz zmierzyć liczbę parametrów akceptowanych przez funkcję. Jeśli masz> n % wszystkich funkcji z> m parametrów, można wybrać się liczyć je jako nie prosty, w zależności od sposobu zdefiniowania n i m (n = 3 może i m = 6?). Myślę, że istnieją pewne narzędzia analizy statycznej, które mogą to zmierzyć - myślę, że JTest po prostu mierzył funkcje z parametrami > m .

  • Możesz spróbować policzyć liczbę zagnieżdżonych pętli lub struktur kontrolnych. Myślę, że to właściwie nie jest zły wskaźnik i myślę, że ma na to swoją nazwę (nie mogę sobie przypomnieć z góry głowy). Ponownie myślę, że istnieją narzędzia (ponownie, takie jak JTest), które mogą to zmierzyć do pewnego stopnia.

  • Możesz spróbować zmierzyć „refaktowalność”. Jeśli Twój kod zawiera wiele fragmentów kodu, które mogą zostać ponownie przetworzone, ale nie są , być może nie byłoby to takie proste. Pamiętam też z czasu, gdy współpracowałem z JTestem, że on również próbował to zmierzyć, ale pamiętam, że często nie zgadzałem się z tym w tym przypadku, więc YMMV.

  • Możesz spróbować zmierzyć liczbę warstw między różnymi częściami systemu. Na przykład: ile różnych fragmentów kodu dotknie danych pochodzących z formularza internetowego, zanim zostanie on zapisany w bazie danych? Właściwy pomiar może być trudny ...

FrustratedWithFormsDesigner
źródło
2
Uważam, że # 3 nazywa się głębokością bloku. Jest to również związane ze złożonością cyklomatyczną, jeśli w grę wchodzą struktury kontroli decyzyjnej.
Thomas Owens
Brak objaśnienia?
FrustratedWithFormsDesigner
Nie mogę się zgodzić z „prostotą funkcji językowych”. Zaawansowane funkcje upraszczają kod. Używanie tylko prostych, podstawowych funkcji zaciemni to, co faktycznie robi kod, co nieuchronnie prowadzi do wycieku warstw abstrakcji. Zaawansowane funkcje językowe pozwalają wyrazić wyższy poziom abstrakcji, pozostawiając kod o wiele bardziej gęsty i czytelny. Im bardziej zaawansowanych funkcji używasz (oczywiście mądrze), tym lepiej dla uproszczenia. Wystarczy porównać kod, powiedzmy, w Matlabie (który jest rzeczywiście „zaawansowany”) z podobnym kodem Fortrana złożonym tylko z podstawowych funkcji.
SK-logic,
Nie zgadzam się również z liczbą warstw. Jeśli możesz zrobić coś w tuzinie trywialnych i czystych kroków lub w jednej pokręconej transformacji, lepiej zrób to w kilku krokach. Wiele prostych i wyraźnie oddzielonych warstw jest znacznie lepszych (i prostszych) niż pojedyncza skręcona warstwa.
SK-logic,
@ SK-logic: Chyba powinienem nazwać to „sprytem”, jest bliższe temu, co miałem na myśli. Powiedziałbym tylko, że takie rzeczy ?:są problemem, gdy są zagnieżdżone 5 głęboko. Jeśli chodzi o warstwy, czysto oddzielone warstwy są lepsze niż jedna zwinięta warstwa. Ale 7 przeważnie nadmiarowych warstw, gdy potrzebne były tylko 2 lub 3, to zła rzecz.
FrustratedWithFormsDesigner