Macierz zależności (Design structure matrix) w nDepend – interpretacja

W poprzednim wpisie przedstawiłem podstawy macierzy DSM. Dzisiaj postaram się wyjaśnić, jakie znaczenie ma to w praktyce. Sam fakt, że macierz pokazuje referencje między różnymi elementami systemu nie jest zbyt ciekawy.

Pierwszą własność, jaką w łatwy sposób możemy odczytać z DSM jest spójność (cohesion). Dokładna wartość spójności zależy od konkretnej metryki. Ogólnie pisząc, wysoka spójność w systemie oznacza, że elementy w nim są bardzo od siebie zależne. Przyjrzyjmy się następującemu przykładowi (źródło: dokumentacja nDepend):

 

Jeśli na macierzy DSM można dostrzec grupy, tworzące kwadrat, wtedy oznacza to, że są one ścisłe ze sobą połączone. W takim przypadku warto zastanowić się nad scaleniem bibliotek w jeden dll i podzielenie komponentów na namespace. Warto zaznaczyć, że obiekty DLL powinny służyć do podziału fizycznego a nie logicznego. Jeśli zatem wiemy, że mamy kilka niezależnych od siebie komponentów, nie oznacza to, że należy je umieścić w osobnych DLL. Podziału logicznego w .NET dokonujemy np. za pomocą przestrzeni nazw. Praca z zbyt wieloma projektami jest kłopotliwa – Visual Studio znacząco spowalnia, deployment jest zwykle trudniejszy. Utrzymanie takich aplikacji jest po prostu bardziej czasochłonne. Kiedy zatem warto umieszczać kod w osobnych bibliotekach? Jak wspomniałem, DLL jest podziałem fizycznym. Jeśli musimy zainstalować dany komponent w kilku różnych instalatorach, wtedy umieszczamy kod w osobnych dll.  Wyobraźmy, sobie, że mamy logikę, która jest dostępna tylko w wersjach premium danej aplikacji. Wtedy oczywiście taka logika, musi być zaimplementowana za pomocą pluginów  aby uzyskać większą elastyczność podczas ładowania obiektów. Jeśli jednak wiemy, że dwa komponenty (mimo, że są niezależne) zawsze muszą być razem zainstalowane oraz istnieje duża szansa, że załadowanie jednego, spowoduje za chwile załadowanie drugiego, wtedy nie ma powodu, aby umieszczać je w osobnych dll, które powodują wiele problemów (wydajność, utrzymanie, jakość pracy). Pamiętajmy jednak, że jeśli nie korzystamy z osobnych AppDomain to wtedy nie ma możliwości usunięcia z pamięci załadowanej już biblioteki. Z tego względu, jeśli komponent B jest używany sporadycznie, a komponent A bardzo często,  warto zastanowić się nad osobnymi bibliotekami lub AppDomain (jeśli oczywiście istnieje uzasadniona potrzeba izolacji kodu).

Wróćmy jednak do spójności. Wysoka spójność oznacza, że komponenty ze sobą są bardzo powiązane. W takiej sytuacji, prawie pewne jest, że chcemy mieć je w jednej bibliotece. Nie ma sensu tworzyć 5 bibliotek, w których wykonywane są podobne operacje i wymagane są relacje do pozostałych dll.

Kiedyś opisywałem zasady SOLID. Jedna z nich mówi o pojedynczej odpowiedzialności.W praktyce dość trudno jednoznacznie określić co to jest pojedyncza odpowiedzialność. Istnieje wiele metryk, które pomagają w tym. Za pomocą DSM jednak, możemy zobaczyć to co powinno być oczywiste – jeśli klasa\komponent A posiada dużo referencji do innych klas, wtedy być może skupia w sobie zbyt wiele odpowiedzialności? Komponenty z zbyt wieloma odpowiedzialnościami są bardzo trudne w utrzymaniu. Wyobraźmy sobie projekt z 100 klas. Jeśli jedna klasa miałaby referencje do pozostałych 99, wtedy modyfikacja czegokolwiek w systemie zmusza nas do przetestowania tej jednej klasy. Przyjrzyjmy się następującej DSM (źródło: dokumentacja nDepend):

Jeśli dany wiersz ma wiele pól zielonych (albo kolumna ma wiele pól niebieskich) wtedy dana klasa ma dużo referencji. Szukamy zatem wierszy\kolumn które mają bardzo wiele zamalowanych pól. Każde takie pole, oznacza kolejną referencję.

W systemach istnieją obiekty niestety, od których wiele klas będzie zależna. Wyobraźmy sobie wszystkie systemowe typy jak String czy Integer. Zmiana kodu w nich, spowoduje, że aplikacje potencjalnie mogą zostać złamane. Warto być świadomym takich obiektów i jeśli zmiana ich jest nieunikniona, wtedy wiadomo przynajmniej jakie inne klasy powinny być przetestowane. Jeśli dana kolumna posiada dużo zielonych pól lub dany wiersz dużo niebieskich pól, wtedy modyfikacja takiego typu, powinna być przeprowadza szczególnie uważnie.

Jeśli nie jest dla Was jasne, kiedy pola są zielone a kiedy niebieskie, zachęcam do przeczytania poprzedniego wpisu. Na początku może to być trochę skomplikowane – mam na myśli identyfikację klas które mają zbyt wiele odpowiedzialności oraz klas, które są używane bardzo często. Proszę zauważyć, że są to przeciwne scenariusze. Zbyt wiele odpowiedzialności zwykle jest złą praktyką, z kolei druga sytuacja zdarza się dość często – jak to ma miejsce w przypadku string. Z tego względu, kolory również odczytujemy odwrotnie. Zachęcam również do wyciągnięcia kartki i narysowania kilku własnych macierzy, które pomogą rozróżnić sytuacje tzw. god class (zbyt wiele odpowiedzialności) oraz po prostu popularny kod(String itp.).

Ostatnim przykładem są cykle, o których napisałem już w poprzednim w poście. Dla przypomnienia tylko dodam, że macierz musi być trójkątna co oznacza, że nie ma cykli. nDepend działa jednak trochę inaczej i musimy po prostu unikać czarnych pól, które oznaczają wzajemne relacje (A->B,B->A).

Leave a Reply

Your email address will not be published.