Die Serie
Einführung |
Terminologie |
Werkzeugunterstützung für Java |
Wo liegt eigentlich das Problem? (dieser Artikel) |
Einfluss auf Qualitätsmerkmale |
Die Praxis |
Verschiedene Erscheinungsformen |
Zusammenhang mit der Pakethierarchie |
Zusammenhang mit der Domäne |
Einige Metriken |
Durchbrechung von Zyklen |
Das Prinzip |
Transitive Abhängigkeiten
Zunächst möchte ich eine Betrachtungsweise für Abhängigkeiten erläutern, die es ermöglicht, den Umfang der Abhängigkeiten eines Gesamtsystems besser vergleichen zu können. Diese Vergleichbarkeit wird ein besseres Verständnis der Auswirkungen zyklischer Abhängigkeiten ermöglichen. Betrachten wir die folgende Konstellation:In beiden Fällen zählen wir drei Elemente und zwei direkte Abhängigkeiten. Zählen wir aber die Anzahl der Elemente, von denen andere Elemente abhängen, so gelangen wir in Abbildung 1a auf drei Elemente und in Abbildung 1b lediglich auf zwei. Wir betrachten hierzu für jedes Einzelelement, wie viele Elemente von ihm aus erreicht werden können, und summieren diese Anzahlen auf. Von A aus lassen sich B und C erreichen, also zwei Elemente. Von B aus lässt sich zusätzlich C erreichen, so dass wir insgesamt zur Summe drei gelangen.
Die vorgeschlagene Zählweise berücksichtigt also auch transitive Abhängigkeiten und enthält daher mehr Information als die reine Aufsummierung der Einzelabhängigkeiten. Sie geht meines Wissens auf John Lakos (siehe [Lakos1996]) zurück, der sie zur Beurteilung der Abhängigkeiten zwischen Components (für ihn in C++ eine Kombination aus .h- und .cpp-Datei) in Packages einführt und als Cumulative Component Dependency (CCD) bezeichnet. H. Melton und E. Tempero verwenden in [Melton2007] eine auf Java-Klassen bezogene Variante, um zyklische Abhängigkeiten zwischen Klassen zu untersuchen. Sie bezeichnen diesen Wert als Class Reachability Set Size (CRSS), was sich im Deutschen am besten als "Anzahl der erreichbaren Klassen" übersetzen lässt. Im Unterschied zu Lakos' CDD bezieht sich die CRSS auf eine einzelne Klasse.
Für die folgende Betrachtung werde ich die beschriebene Zählweise nicht exklusiv auf Klassen oder andere konkrete Entitäten, sondern auf beliebige Software-Elemente beziehen, die einen Abhängigkeitsgraphen bilden können. Ich verwende dabei analog zu Lakos die Summe über alle Elemente des Systems. Diese angepasste Metrik bezeichne ich im Folgenden kurz als Reachability.
Zyklen als spezielle transitive Abhängigkeiten
Wenden wir die beschriebene Zählweise nun auf zyklische Abhängigkeiten an und betrachten den folgenden kleinen Zyklus dreier Elemente und eine "durchbrochene" Variante.Für das Beispiel in 2a nehmen wir an, dass es sich bei den Elementen um Klassen handelt. In Abbildung 2b wurde der Klassen-Zyklus aus 2a per Dependency Inversion durchbrochen. C verwendet nun das Interface I, das von A implementiert wird. Würde es sich in 2a nicht um Klassen handeln, sondern um Pakete oder physische Einheiten, so würde sich ein etwas anderes Durchbrechungs-Muster ergeben. Wir möchten nun diese beiden Varianten hinsichtlich der Anzahl ihrer Elemente und Abhängigkeiten vergleichen. Dabei ergibt sich Folgendes:
- In der zyklischen Variante existieren drei Elemente und drei Abhängigkeiten, in der azyklischen Version vier Elemente und vier Abhängigkeiten.
- Die Gesamtzahl der Elemente und Einzelabhängigkeiten ist in der Lösung 2b also um eins erhöht.
- Das Verhältnis der Anzahl der Elemente zur Anzahl der Abhängigkeiten ist gleich geblieben.
- Bei Berücksichtigung der transitiven Abhängigkeiten hat sich die Situation in 2b jedoch bereits etwas verbessert. In Abbildung 2a sind von jedem Element aus zwei andere Elemente erreichbar, also insgesamt sechs Elemente. In Abbildung 2b sind von A und C aus jeweils zwei Elemente erreichbar, von B aus nur ein Element, also insgesamt fünf Elemente - immerhin eines weniger.
Das Beispiel legt auch nahe, dass die Reachability mit steigender Zahl der Elemente stärker ansteigt als die Anzahl der Elemente und Einzelabhängigkeiten, die für die Durchbrechung eingeführt werden müssen. Dies erklärt sich graphentheoretisch wie folgt:
- Definitionsgemäß steht in einem Zyklus jedes Element mit jedem anderen Element zumindest transitiv in Beziehung. Die Zahl dieser Beziehungen berechnet sich mit der Formel n * (n - 1), so dass die Reachability in einem Zyklus immer quadratisch ansteigt.
- Außer in einer sogenannten "Clique", in der jedes Element eines Zyklus mit jedem anderen Element einen direkten Teilzyklus eingeht, ist die Zahl der zu durchbrechenden Teilzyklen jedoch immer kleiner als n * (n - 1). Wie groß sie genau ist, hängt von der Anzahl der Teilzyklen im Gesamtzyklus ab, die durchbrochen werden müssen. Geht man zusätzlich davon aus, dass die Kosten für eine einzelne Durchbrechung konstant sind, wie es bei Dependency Inversion der Fall ist, so steigen die Größenmaße außer für Cliquen in jedem Fall schwächer an als die Reachability.
Wo liegt also das Problem?
Die obigen Betrachtungen zeigen, dass insbesondere bei größeren Zyklen die Reachability innerhalb des Zyklus gegenüber derjenigen einer äquivalenten zyklenfreien Struktur stark ansteigt. Dies resultiert in den folgenden beiden Problemgruppen:- Infizierbarkeit: Negative Eigenschaften oder erforderliche Änderungen einzelner Elemente können eine größere Menge anderer Elemente beeinflussen. Beispiele für negative Eigenschaften sind Fehleranfälligkeit, hohe Änderungsraten, Inflexibilität, schlechte Laufzeit usw. Beispiele für erforderliche Änderungen sind modifizierte Methodensignaturen oder eine geänderte Semantik einzelner Methoden.
- Schwergewichtigkeit: Es wird schwieriger, Entwicklungsaktivitäten auf kleinere, ausgewählte Gruppen von Elementen zu beziehen. Die Element-Gruppen, mit denen jeweils umgegangen werden muss, werden somit tendentiell größer und schwerer handhabbar. Beispiele für solche Entwicklungsaktivitäten sind Verstehen, Test, Änderung, Wiederverwendung, Build usw.
Quellen
[Dietrich2014] - On The Shape of Circular Dependencies in Java Programs, Al-Mutawa H., Dietrich J., Marsland S., McCartin, C., Proceedings ASWEC'14, Preprint (2014)[Lakos1996] - Large-Scale C++ Software Design, John Lakos (1996)
[Melton2007] - The CRSS metric for package design quality, H. Melton, E. Tempero, ACSC '07 Proceedings of the thirtieth Australasian conference on Computer science - Volume 62 (2007), siehe https://www.cs.auckland.ac.nz/~hayden/MeltonTemperoISESE06.pdf