Komplexe Java-Anwendungen bestehen normalerweise aus einzelnen Komponenten. In diesen sind fachliche Logik, Benutzeroberfläche, die Datenhaltungsschicht oder auch Schnittstellen zu externen Systemen gekapselt.
Den einzelnen Komponenten können dann klar umrissene Verantwortlichkeiten zugeteilt werden. Dies verhindert Redundanzen, da Code für ein und dieselbe Aufgabe nur in genau einer Komponente implementiert werden muss. Diese stellt ihre Funktionalität dann über eine klar definierte Schnittstelle nach außen zur Verfügung. Andere Komponenten nutzen diese Schnittstelle, ohne diese Funktionalität noch einmal selbst implementieren zu müssen.
Somit ergeben sich Abhängigkeiten zwischen Komponenten in einer Anwendung, die beispielsweise in UML durch die uses-Relation dargestellt werden.
Dabei muss darauf geachtet werden, dass zwischen den Komponenten keine zirkulären Abhängigkeiten entstehen, d.h., zwei Komponenten dürfen keine gegenseitigen Abhängigkeiten haben.
Zirkuläre Abhängigkeiten können, wie in Abbildung 2, direkt vorkommen oder auch indirekt, wie in Abbildung 3.
Zirkuläre Abhängigkeiten auf Klassenebene führen im schlimmsten Fall zu Totschleifen oder Memory-Leaks. Auf Komponentenebene sind sie meist ein Indiz für schlechtes Design, wie etwa ungünstig verteilte Zuständigkeiten. Meist sind es Implementierungsfehler der Art, dass Anwendungslogik am falschen Ort, also nicht in der dafür vorgesehenen Komponente liegt.
In jedem Fall sollten zirkuläre Abhängigkeiten jedoch, sofern sie bekannt sind, Anlass für ein Code-Refactoring sein.
Das Sonar suchen lassen
Ein ideales Werkzeug zum Aufdecken dieser Abhängigkeiten ist Sonar und dort speziell der Bereich Design.
Im Design-Bereich sehen wir eine Matrix, in der die X- und die Y-Achse jeweils die einzelnen Komponenten des aktuellen Projektes darstellen. Aus Platzgründen ist nur die Y-Achse beschriftet. Auf der X-Achse sind jedoch dieselben Komponenten aufgelistet. Zeile 1 und Spalte 1 sind im hier gezeigten Beispiel jeweils die Komponente de.ejb3buch.ticket2rock. Am Schnittpunkt von Zeile 1 und Spalte 1 wird deswegen ein Bindestrich angezeigt.
An anderen Schnittpunkten zwischen unterschiedlichen Komponenten sehen wir ein leeres Kästchen, wenn keinerlei Abhängigkeiten zwischen Komponenten bestehen. Bestehen Abhängigkeiten, so wird im Schnittpunkt die Anzahl der Zugriffe der Kompontente aus der X-Achse auf die Komponente aus der Y-Achse angezeigt.
In Abbildung 5 sehen wir zum Beispiel relativ weit oben links, dass de.ejb3buch.ticket2rock.applikation.controller viermal auf de.ejb3buch.ticket2rock.applikation.helper zugreift.
Interessant sind in dieser Übersicht die rot markierten Felder. Diese zeigen zirkuläre Abhängigkeiten.
Ein Klick auf das rot markierte Feld zeigt anschließend durch weitere farbliche Markierungen an, zwischen welchen Komponenten genau die zirkuläre Abhängigkeit besteht. Dies ist in Abbildung 6 dargestellt.
In unserem Beispiel sind dies:
- de.ejb3buch.ticket2rock.exception
- de.ejb3buch.ticket2rock.entity
Durch ein Code-Refactoring sollte diese Abhängigkeit aufgelöst werden. Mit wenigen weiteren Mausklicks führt Sonar direkt zu den Zeilen im Quellcode, die die Abhängigkeiten verursachen.
Fazit
In komplexen Anwendungen ist es ohne technische Hilfe kaum möglich, zirkuläre Abhängigkeiten ausfindig zu machen. Sie kommen erfahrungsgemäß nicht selten vor und das Ziel sollte sein, sie zu finden und zu beseitigen. Sonar ist für diese Aufgabe ein ideales Werkzeug.
Der Installationsaufwand dafür wird sich schnell wieder durch bessere Code-Qualität amortisieren. Siehe dazu auch unseren Artikel Was kostet Software Lifecycle Management – wenn man darauf verzichtet?