Jak ściśle przestrzegasz zasady „No Dependency Cycle” (NDepend)

10

Trochę tła: Jako lider zespołu używam NDepend raz w tygodniu, aby sprawdzić jakość naszego kodu. Szczególnie nieocenione jest dla mnie pokrycie testu, wiersze kodu i wskaźniki złożoności cyklicznej. Ale jeśli chodzi o cykle wyrównywania i zależności, jestem trochę ... bardzo zaniepokojony. Patrick Smacchia ma fajny post na blogu, który opisuje cel wyrównania.

Żeby było jasne: w „cyklu zależności” rozumiem cykliczne odniesienia między dwiema przestrzeniami nazw.

Obecnie pracuję nad strukturą GUI opartą na systemie Windows CE dla wbudowanych instrumentów - pomyśl tylko o platformie graficznej dla Androida, ale dla bardzo niskich instrumentów. Framework jest pojedynczym zestawem zawierającym około 50 000 linii kodu (bez testów). Struktura jest podzielona na następujące przestrzenie nazw:

  • Podstawowy podsystem nawigacji i menu
  • Podsystem ekranu (Prezenterzy / Widoki / ...)
  • Warstwa kontrolna / warstwa widgetu

Dzisiaj spędziłem pół dnia, próbując doprowadzić kod do właściwego poziomu [dzięki Resharperowi nie ma problemu w ogóle], ale we wszystkich przypadkach istnieją pewne cykle zależności.

Więc moje pytanie: jak ściśle przestrzegasz zasady „Brak cyklu zależności”? Czy wyrównanie naprawdę jest tak ważne?

ollifant
źródło
2
Jeśli „brak cyklu zależności” oznacza brak odwołań cyklicznych, to - bez wymówek. To jest czyste zło.
Arnis Lapsa
@ollifant: zdefiniuj, co dokładnie masz na myśli, pisząc „Bez cyklu zależności”. Między warstwami czy między klasami w warstwie?
Doc Brown

Odpowiedzi:

9

Niedawno napisałem 2 białe książki, opublikowane na Simple-Talk na temat architektury kodu .NET (pierwsza książka dotyczy zespołów .NET, druga - przestrzeni nazw i poziomowania):

Partycjonowanie bazy kodu za pomocą zespołów .NET i projektów Visual Studio

Definiowanie komponentów .NET z przestrzeniami nazw

Czy wyrównanie naprawdę jest tak ważne?

Tak to jest!

Cytat z drugiej białej księgi:

Jeśli wykres zależności między komponentami zawiera cykl, komponentów uczestniczących w cyklu nie można opracować i przetestować niezależnie. Z tego powodu cykl składników reprezentuje superkomponent, z wyższą entropią niż suma entropii zawartych w nim składników.

(...)

Tak długo, jak acykliczne ograniczenie komponentów jest stale przestrzegane, podstawa kodu pozostaje wysoce przyswajalna i łatwa do utrzymania.

  • W tradycyjnej architekturze budynku siła grawitacji wywiera presję na artefakty niskiego poziomu. To sprawia, że ​​są bardziej stabilne: „stabilne” w tym sensie, że trudno je przenieść.
  • W architekturze oprogramowania przestrzeganie idei acyklicznych komponentów wywiera presję na komponenty niskiego poziomu. To sprawia, że ​​są bardziej stabilne, ponieważ ich refaktoryzacja jest bolesna. Empirycznie abstrakcje rzadziej podlegają refaktoryzacji niż implementacjom. z tego powodu dobrym pomysłem jest to, że komponenty niskiego poziomu zawierają głównie abstrakcje (interfejsy i wyliczenia), aby uniknąć bolesnego refaktoryzacji.
Patrick Smacchia - twórca NDepend
źródło
6

Nigdy nie dopuszczam okrągłych zależności między klasami lub przestrzeniami nazw. W języku C #, Java i C ++ zawsze możesz przerwać zależność od klasy okrągłej, wprowadzając interfejs.

Kodowanie w pierwszej kolejności utrudnia wprowadzenie zależności cyklicznych.

Kevin Cline
źródło
4

To zawsze zawód: musisz zrozumieć koszt zależności, aby zrozumieć, dlaczego należy unikać zależności. W ten sam sposób, jeśli rozumiesz koszt zależności, możesz lepiej zdecydować, czy jest ona warta w konkretnym przypadku.

Na przykład w grach konsolowych często polegamy na zależnościach, w których potrzebne są bliskie relacje informacji, głównie ze względu na wydajność. W porządku, o ile nie musimy być tak elastyczni jak na przykład narzędzie do edycji.

Jeśli rozumiesz ograniczenia związane z uruchomieniem oprogramowania (sprzęt, system operacyjny lub po prostu projektowanie (np. „Łatwiejszy interfejs użytkownika”)), łatwo jest wybrać, które zależności nie powinny zostać wykonane, a które ok.

Ale jeśli nie masz dobrego i jasnego powodu, unikaj jakiejkolwiek zależności. Kod zależny to piekło sesji debugowania.

Klaim
źródło
2

Za każdym razem, gdy omawiany jest ten temat, uczestnicy zwykle tracą rozróżnienie między cyklami kompilacji i cykli wykonawczych. To pierwsze jest tym, co John Lakos nazwał „fizycznym projektem”, podczas gdy drugie jest w zasadzie nieistotne w dyskusji (nie rozłączaj się z cyklami czasu wykonywania, takimi jak te wywołane wywołaniami zwrotnymi).

To powiedziawszy, John Lakos był bardzo surowy w eliminowaniu wszystkich cykli (czasu kompilacji). Jednak Bob Martin przyjął postawę, że tylko cykle między plikami binarnymi (np. DLL, pliki wykonywalne) są znaczące i należy ich unikać; uważał, że cykle między klasami w pliku binarnym nie są strasznie ważne.

Osobiście uważam pogląd Boba Martina na ten temat. Nadal jednak zwracam uwagę na cykle między klasami, ponieważ brak takich cykli ułatwia innym czytanie i uczenie się kodu.

Należy jednak zauważyć, że żaden kod tworzony za pomocą programu Visual Studio nie może mieć cyklicznych zależności między plikami binarnymi (kod macierzysty lub zarządzany). Zatem najpoważniejszy problem z cyklami został dla ciebie rozwiązany. :)

Brent Arias
źródło
0

Niemal całkowicie, ponieważ cykle zależności typu kompilacji są niewygodne w Haskell, a niemożliwe w Agdzie i Coq, i są to języki, których zwykle używam.

Robin Green
źródło