Wie streng halten Sie sich an die Regel „Kein Abhängigkeitszyklus“ (NDepend)?

10

Ein bisschen Hintergrundwissen: Als Teamleiter benutze ich NDepend ungefähr einmal pro Woche, um die Qualität unseres Codes zu überprüfen. Insbesondere die Testabdeckung, Codezeilen und zyklomatische Komplexitätsmetriken sind für mich von unschätzbarem Wert. Aber wenn es um Levelisierungs- und Abhängigkeitszyklen geht, bin ich ein bisschen ... sehr besorgt. Patrick Smacchia hat einen schönen Blog-Beitrag, der das Ziel der Levelisierung beschreibt.

Um es klar zu sagen: Unter "Abhängigkeitszyklus" verstehe ich Zirkelverweise zwischen zwei Namespaces.

Derzeit arbeite ich an einem Windows CE-basierten GUI-Framework für eingebettete Instrumente - denken Sie nur an die Android-Grafikplattform, aber für sehr einfache Instrumente. Das Framework ist eine einzelne Baugruppe mit ca. 50.000 Codezeilen (ohne Tests). Das Framework ist in folgende Namespaces unterteilt:

  • Kernnavigations- und Menüsubsystem
  • Bildschirm-Subsystem (Moderatoren / Ansichten / ...)
  • Steuerelemente / Widget-Ebene

Heute habe ich den halben Tag damit verbracht, den Code auf das richtige Niveau zu bringen [dank Resharper im Allgemeinen kein Problem], aber in allen Fällen existieren einige Abhängigkeitszyklen.

Meine Frage: Wie streng befolgen Sie die Regel "Kein Abhängigkeitszyklus"? Ist Levelisierung wirklich so wichtig?

ollifant
quelle
2
Wenn "kein Abhängigkeitszyklus" keine Zirkelverweise bedeutet, dann - keine Ausreden. Das sind reine Übel.
Arnis Lapsa
@ollifant: Definieren Sie genau, was Sie meinen, wenn Sie "No Dependency Cycle" schreiben. Zwischen Ebenen oder zwischen Klassen innerhalb einer Ebene?
Doc Brown

Antworten:

9

Ich habe kürzlich zwei Whitebooks geschrieben, die auf Simple-Talk zum Thema Architektur von .NET-Code veröffentlicht wurden (das erste Buch befasst sich mit .NET-Assemblys, das zweite mit Namespaces und Levelisierung):

Partitionieren Ihrer Codebasis über .NET-Assemblys und Visual Studio-Projekte

Definieren von .NET-Komponenten mit Namespaces

Ist Levelisierung wirklich so wichtig?

Ja ist es!

Zitat aus dem 2. Weißbuch:

Wenn das Diagramm der Abhängigkeiten zwischen Komponenten einen Zyklus enthält, können die am Zyklus beteiligten Komponenten nicht unabhängig voneinander entwickelt und getestet werden. Aus diesem Grund stellt der Zyklus der Komponenten eine Superkomponente dar, deren Entropie höher ist als die Summe der Entropien der enthaltenen Komponenten.

(...)

Solange die Einschränkung der azyklischen Komponenten kontinuierlich eingehalten wird, bleibt die Codebasis in hohem Maße lernbar und wartbar.

  • In der traditionellen Gebäudearchitektur übte die Schwerkraft Druck auf Artefakte auf niedrigem Niveau aus. Dies macht sie stabiler: "stabil" in dem Sinne, dass sie schwer zu bewegen sind.
  • In der Softwarearchitektur übt die Einhaltung der Idee der azyklischen Komponenten Druck auf Komponenten auf niedriger Ebene aus. Dies macht sie stabiler in dem Sinne, dass es schmerzhaft ist, sie umzugestalten. Empirisch werden Abstraktionen seltener umgestaltet als Implementierungen. Aus diesem Grund ist es eine gute Idee, dass Komponenten auf niedriger Ebene hauptsächlich Abstraktionen (Schnittstellen und Aufzählungen) enthalten, um schmerzhaftes Refactoring zu vermeiden.
Patrick Smacchia - NDepend dev
quelle
6

Ich erlaube niemals zirkuläre Abhängigkeiten zwischen Klassen oder Namespaces. In C #, Java und C ++ können Sie eine zirkuläre Klassenabhängigkeit jederzeit durch Einführung einer Schnittstelle aufheben.

Das Codieren von Test-First macht es schwierig, zirkuläre Abhängigkeiten einzuführen.

Kevin Cline
quelle
4

Es ist immer ein Kompromiss: Sie müssen die Kosten von Abhängigkeiten verstehen, um zu verstehen, warum Abhängigkeiten vermieden werden sollen. Wenn Sie die Kosten einer Abhängigkeit verstehen, können Sie auf die gleiche Weise besser entscheiden, ob sie sich in Ihrem speziellen Fall lohnt.

Beispielsweise verlassen wir uns in Konsolenvideospielen häufig auf Abhängigkeiten, bei denen enge Informationsbeziehungen erforderlich sind, hauptsächlich aus Leistungsgründen. Das ist in Ordnung, sofern wir nicht so flexibel sein müssen wie beispielsweise ein Edition-Tool.

Wenn Sie die Einschränkungen verstehen, unter denen Ihre Software ausgeführt werden muss (Hardware, Betriebssystem oder einfach nur Design (wie "die einfachere Benutzeroberfläche, die wir können")), sollte es einfach sein, zu verstehen, welche Abhängigkeiten nicht ausgeführt werden sollten und welche okay.

Wenn Sie jedoch keinen guten und klaren Grund haben, vermeiden Sie jede mögliche Abhängigkeit. Abhängiger Code ist die Hölle der Debug-Sitzung.

Klaim
quelle
2

Wenn dieses Thema besprochen wird, verlieren die Teilnehmer normalerweise den Ort der Unterscheidung zwischen Erstellungs- und Laufzeitzyklen. Ersteres ist das, was John Lakos als "physisches Design" bezeichnete, während letzteres für die Diskussion im Grunde genommen irrelevant ist (lassen Sie sich nicht auf Laufzeitzyklen ein, wie sie durch Rückrufe entstehen).

John Lakos war jedoch sehr streng darin, alle (Bauzeit-) Zyklen zu eliminieren. Bob Martin vertrat jedoch die Auffassung, dass nur Zyklen zwischen Binärdateien (z. B. DLLs, ausführbare Dateien) von Bedeutung sind und vermieden werden sollten. Er glaubte, dass Zyklen zwischen Klassen innerhalb einer Binärdatei nicht besonders wichtig sind.

Ich persönlich halte Bob Martins Ansicht dazu. Ich widme den Zyklen zwischen den Klassen jedoch immer noch einige Aufmerksamkeit, da das Fehlen solcher Zyklen das Lesen und Lernen des Codes für andere erleichtert.

Es sollte jedoch darauf hingewiesen werden, dass Code, den Sie mit Visual Studio erstellen, keine zirkulären Abhängigkeiten zwischen Binärdateien aufweisen kann (ob nativer oder verwalteter Code). Damit ist das schwerwiegendste Problem mit Zyklen für Sie gelöst. :) :)

Brent Arien
quelle
0

Fast vollständig, da Abhängigkeitszyklen vom Build-Typ in Haskell unpraktisch und in Agda und Coq unmöglich sind. Dies sind die Sprachen, die ich normalerweise verwende.

Robin Green
quelle