Abhängigkeitsstrukturmatrix (DSM)
Code-Organisation verbessern mit der Dependency Structure Matrix
Einführung
Die DSM (Dependency Structure Matrix) ist eine kompakte Art, Abhängigkeiten zwischen Komponenten darzustellen und durch sie zu navigieren. Für die meisten Ingenieure bedeutet das Sprechen über Abhängigkeiten das Sprechen über etwas, das so aussieht:

DSM wird verwendet, um dieselben Informationen wie ein Graph darzustellen.
- Matrix-Header-Elemente stellen Graph-Boxen dar
- Nicht leere Matrixzellen entsprechen den Pfeilen des Graphen.
Folglich ist im Snapshot unten die Kopplung von Netto zu Foundation wird durch eine nicht leere Zelle in der Matrix und einen Pfeil im Graphen dargestellt.

Warum zwei verschiedene Wege — Graph und DSM — nutzen, um dieselbe Information darzustellen? Weil es einen Trade-off gibt:
- Der Graph ist intuitiver, kann aber völlig unverständlich werden, wenn die Anzahl der Knoten und Kanten wächst (ein paar Dutzend Boxen können ausreichen, um einen zu komplexen Graphen zu erzeugen)
- DSM ist weniger intuitiv, kann aber sehr effizient sein, um große und komplexe Graphen darzustellen. Wir sagen, DSM skaliert mit dem Graphen vergleichen.
Sobald man die DSM-Prinzipien verstanden hat, bevorzugt man typischerweise DSM gegenüber Graphen zur Darstellung von Abhängigkeiten. Dies liegt hauptsächlich daran, dass DSM die Möglichkeit bietet, strukturelle Muster auf einen Blick erkennen Dies wird in der zweiten Hälfte dieses Dokuments erklärt.
CppDepend bietet Kontextsensitive Hilfe um den Benutzer über das aufzuklären, was er auf der DSM sieht. Die DSM von CppDepend basiert auf einem einfachen 3-Farben-Schema für DSM-Zellen: Blau, Grün und Schwarz. Beim Überfahren einer Zeile oder Spalte mit der Maus erklärt die kontextsensitive Hilfe die Bedeutung dieses Farbschemas:

Eine nicht-leere DSM-Zelle enthält eine Zahl. Diese Zahl repräsentiert die Stärke der durch die Zelle dargestellten Kopplung. Die Kopplungsstärke kann als Anzahl der an der Kopplung beteiligten Member/Methoden/Felder/Typen oder Namespaces ausgedrückt werden, abhängig vom tatsächlichen Wert der Option Gewicht auf Zellen Zusätzlich zur kontextsensitiven Hilfe bietet das DSM auch Info-Panel das Kopplung mit einer Beschreibung in einfachem Englisch erklärt:

Das DSM von CppDepend bietet zahlreiche Optionen zum Ausprobieren:
- Es bietet zahlreiche Möglichkeiten, in die Abhängigkeitserkundung einzutauchen (eine übergeordnete Spalte/Zeile kann geöffnet, Zellen können expandiert werden...)
- Es kann mit quadratischen symmetrischen DSM und rechteckigen nicht-symmetrischen DSM umgehen
- Horizontale und vertikale Header können gebunden werden, um stets eine quadratische symmetrische Matrix zu erhalten
- Es bietet die Option Indirekte Verwendung, wobei Zellen direkte und indirekte Verwendung zeigen
- Der vertikale Header kann Tier-Code-Elemente enthalten
- ...
Es wird empfohlen, all diese Funktionen selbst auszuprobieren, indem Sie Abhängigkeiten in Ihrer Codebasis analysieren.
Codestrukturmuster auf der Matrix identifizieren
Wie in der Einführung erläutert, bietet DSM die Besonderheit, gängige Codestrukturmuster leicht identifizieren zu können. Lassen Sie uns die häufigsten Szenarien vorstellen:
Geschichteter Code
Ein Muster, das eine DSM offensichtlich macht, ist geschichtete Struktur (d. h. azyklische Struktur). Wenn die Matrix dreieckig ist, mit allen blauen Zellen im unteren linken Dreieck und allen grünen Zellen im oberen rechten Dreieck, dann zeigt dies, dass die Struktur perfekt geschichtet ist. Mit anderen Worten: Die Struktur enthält keinen Abhängigkeitszyklus.

Im rechten Teil des Snapshots ist dieselbe geschichtete Struktur als Graph dargestellt. Alle Pfeile haben dieselbe Richtung von links nach rechts. Das Problem bei Graphen ist, dass das Graph-Layout nicht skaliert. Hier können wir das Gesamtbild der Struktur kaum erkennen. Würde die Anzahl der Boxen verdoppelt, wäre der Graph völlig unlesbar. Die DSM-Darstellung wäre dagegen nicht betroffen; wir sagen, dass DSM skaliert besser als Graph.
Randbemerkung: Interessanterweise beruhen die meisten Graph-Layout-Algorithmen auf der Tatsache, dass ein Graph azyklisch ist. Um das Layout eines Graphen mit Zyklen zu berechnen, verwerfen diese Algorithmen vorübergehend einige Abhängigkeiten, um mit einem geschichteten Graphen zu arbeiten, und fügen die verworfenen Abhängigkeiten im letzten Berechnungsschritt wieder hinzu.
Abhängigkeitszyklus
Wenn eine Struktur einen Zyklus enthält, wird der Zyklus durch ein rotes Quadrat auf der DSM angezeigt. Wir können sehen, dass innerhalb des roten Quadrats grüne und blaue Zellen über die Diagonale gemischt sind. Es gibt auch einige schwarze Zellen, die gegenseitige direkte Nutzung darstellen (d. h. A verwendet B und B verwendet A).

Das DSM von CppDepend bietet die einzigartige Option Indirekte Abhängigkeit. Eine indirekte Abhängigkeit zwischen A und B bedeutet, dass A etwas verwendet, das etwas verwendet, das etwas verwendet ... das B verwendet. Unten ist dieselbe DSM mit einem Zyklus gezeigt, aber im indirekten Modus. Wir sehen, dass das rote Quadrat nur mit schwarzen Zellen gefüllt ist. Das bedeutet lediglich, dass für beliebige Elemente A und B im Zyklus A und B indirekt und gegenseitig abhängig sind.

Hier ist dieselbe Struktur als Graph dargestellt. Der rote Pfeil zeigt, dass mehrere Elemente gegenseitig voneinander abhängen. Der Graph hilft jedoch nicht dabei, alle am übergeordneten Zyklus beteiligten Elemente hervorzuheben.

Beachten Sie, dass wir in CppDepend eine Schaltfläche bereitgestellt haben, um Zyklen in der DSM hervorzuheben (falls vorhanden). Wenn die Struktur geschichtet ist, bewirkt diese Schaltfläche, die Matrix zu triangularisieren und nicht-leere Zellen so nah wie möglich an der Diagonale zu halten.

Hohe Kohäsion – geringe Kopplung
Die Idee von hoher Kohäsion (innerhalb einer Komponente) / loser Kopplung (zwischen Komponenten) ist heutzutage populär. Aber wenn man Abhängigkeiten nicht messen und visualisieren kann, ist es schwer, Kohäsion und Kopplung konkret zu bewerten. DSM zeigt hohe Kohäsion sehr gut. In der DSM unten ist ein deutliches quadratisches Aggregat um die Diagonale zu sehen. Es bedeutet, dass die im Quadrat beteiligten Elemente eine hohe Kohäsion haben: Sie sind stark voneinander abhängig. Zudem sehen wir, dass sie geschichtet sind, da es keinen Zyklus gibt. Sie sind sicher Kandidaten, um in ein Eltern-Artefakt (wie einen Namespace oder ein Assembly) gruppiert zu werden.
Andererseits spricht die Tatsache, dass die meisten Zellen um das Quadrat herum leer sind, für eine lose Kopplung zwischen den Elementen des Quadrats und anderen Elementen.

In der DSM unten sehen wir 2 Komponenten mit hoher Kohäsion (oberes und unteres Quadrat) und einer ziemlich niedrigen Kopplung zwischen ihnen.

Beim Refactoring kann ein solcher Indikator sehr nützlich sein, um zu erkennen, ob es Möglichkeiten gibt, grobe Komponenten in mehrere feiner granulierte Komponenten aufzuteilen.
Zu viele Verantwortlichkeiten
Der Single-Responsibility-Prinzip (SRP) erfreut sich heutzutage in der Softwarearchitekten-Community wachsender Beliebtheit. Das Prinzip besagt: eine Klasse sollte nicht mehr als einen Änderungsgrund haben. Eine andere Interpretation des SRP ist, dass eine Klasse nicht zu viele verschiedene andere Typen verwenden sollte. Wenn wir die Idee auf andere Ebenen erweitern (Assemblies, Namespaces und Methoden), dann hat ein Codeelement, das Dutzende anderer verschiedener Codeelemente (auf derselben Ebene) verwendet, sicherlich zu viele Verantwortlichkeiten. Oft wird der Begriff God Class oder God Component wird verwendet, um solchen Code zu qualifizieren.
DSM kann helfen, Codeelemente mit zu vielen Verantwortlichkeiten zu identifizieren. Ein solches Codeelement wird durch Spalten mit vielen blauen Zellen und Zeilen mit vielen grünen Zellen dargestellt. Die DSM unten zeigt dieses Phänomen.

Beliebte Codeelemente
Ein populäres Code-Element wird von vielen anderen Code-Elementen verwendet. Populäre Code-Elemente sind unvermeidbar (denken Sie an Zeichenfolge Klasse zum Beispiel), aber ein beliebtes Codeelement ist kein Mangel. Es bedeutet nur, dass es in jeder Codebasis einige zentrale Konzepte gibt, die durch beliebte Klassen repräsentiert werden.
Ein beliebtes Codeelement wird durch Spalten mit vielen grünen Zellen und Zeilen mit vielen blauen Zellen dargestellt. Die DSM unten hebt ein beliebtes Codeelement hervor.

Zu beachten ist: Hält man seine Codestruktur perfekt geschichtet, bleiben beliebte Komponenten natürlicherweise auf niedriger Ebene. Eine beliebte Komponente kann nämlich de facto nicht viele Dinge verwenden, denn da beliebte Komponenten auf niedriger Ebene liegen, können sie nichts auf einer höheren Ebene verwenden. Das würde eine Abhängigkeit von niedriger zu hoher Ebene erzeugen und die azyklische Eigenschaft der Struktur brechen.
Wechselseitig abhängig
Sie können die Kopplung zwischen 2 Komponenten sehen, indem Sie mit der rechten Maustaste auf eine nicht leere Zelle klicken und das Menü Diese Abhängigkeit öffnen.

Wenn die geöffnete Zelle schwarz war wie im Snapshot oben (d. h. wenn A und B gegenseitig abhängig sind), dann enthält die resultierende rechteckige Matrix sowohl grüne als auch blaue Zellen (und eventuell auch schwarze Zellen), wie im Snapshot unten.

In dieser Situation werden Sie oft ein Defizit an grünen oder blauen Zellen bemerken (hier 3 blaue Zellen für 1 grüne Zelle). Das liegt daran, dass zwischen zwei gegenseitig abhängigen Codeelementen oft eine natürliche Ebenenreihenfolge existiert. Betrachten Sie zum Beispiel die System.Threading Namespaces und die System.String Klasse. Sie sind gegenseitig abhängig; beide verlassen sich aufeinander. Aber die Matrix zeigt, dass Threading ist viel stärker abhängig von Zeichenfolge als umgekehrt (es gibt viel mehr blaue Zellen als grüne Zellen). Dies bestätigt die Intuition, dass Threading ist höherstufig als Zeichenfolge.

Verwandte Ressourcen
Testen Sie CppDepend noch heute
Starten Sie Ihre 14-tägige kostenlose Testversion mit vollem Zugriff auf alle Dokumentationsfunktionen. Keine Kreditkarte erforderlich.
