Das grundlegende Prinzip tragender Architekturen ist schnell erläutert: Ein System muss auf allen Skalen in Schichten von Subsystemen unterteilbar sein. Die Subsysteme der Schicht Sn des Gesamtsystems besitzen dabei keine Abhängigkeiten untereinander und keine Abhängigkeiten zu Schichten Sm mit m > n.
Die Wahl der Subsysteme, ihrer Schichtung und ihrer (erlaubten) Abhängigkeiten untereinander sollte zu allen Zeiten im Projekt änderbar sein. Auf allen Skalen heißt: auf jedes Subsystem muss die gleiche Strategie der Unterteilung in Schichten und Subsubsysteme wieder anwendbar sein. Die rekursive Unterteilung in Schichten und Subsysteme endet also, wenn die Subsysteme so klein sind, dass ihre Funktion klar beschrieben werden und ein Mensch sie ohne Probleme sichten, verstehen und warten kann. Bei Java und vielen anderen Sprachen ist das gewöhnlich die Paket-Ebene, ab der eine weitere Unterteilung in Subsysteme unnötig ist.
Das oben skizzierte Bild kann umgesetzt werden wenn Abhängigkeiten zwischen Subsystemen umkehrbar sind. Das ist mit der Hilfe von Interfaces immer möglich - Interfaces sind exakt für diesen Einsatzzweck geschaffen. Zum Beweis genügt es sich klarzumachen, dass eine gerichtete Abhängigkeit zwischen zwei Subsysteme A und B über die Einführung von Interfaces immer umgekehrt werden kann. Im Beispiel ruft A Funktionalität von B auf. Offenbar kennt A Implementierungsdetails von B, A hat Abhängigkeiten zu B:
A -> BZiel ist es nun diese Abhängigkeit zwischen A und B umzukehren. Es werden in B zunächst Interfaces I deklariert, die die Abhängigkeiten abdecken. Formal sind B' und I das, was vorher B war:
A -> (I <- B')Nun werden die I in A integriert und aus A wird A':
(A -> I) <- B' A' <- B'B' nutzt Implementierungsvorlagen in A' und in der Tat kennt A' keine Implementierungsdetails in B'. Das Ziel ist erreicht, aber: ein neuer Punkt kommt ins Spiel - lesen Sie weiter.
Vor der Refaktoringmaßnahme ist B von A gerufen worden. Die Umkehr der Abhängigkeit durch die beschriebene Refaktorisierung wird das nicht ändern. Aus A' wird nun Code von B' gerufen, da A' nichts von B' weiß muss eine dritte Komponente B' in A' bekannt gemachen. Dieses Bekanntmachen erfolgt innerhalb einer Initialisierungsphase mit der Hilfe einer Infrastruktur, die sowohl A' als auch B' kennt. Streng genommen hat man also die unerwünschte Abhängigkeit verlagert an eine andere Stelle in der Hoffnung, sie dort besser beherrschen zu können.
Der beschriebene Vorgang wird Dependency Injection genannt und das nächste Kapitel listet Strategien für Dependency Injection, hier zunächst eine Zusammenfassung:
Der Klassiker zum Thema von Martin Fowler
Dependency Injection mit Java EE 5