Das Problem: zu viele Abhängigkeiten
In einem grossen Software-Projekt sind eine Vielzahl von Abhängigkeiten zwischen den einzelnen Komponenten unvermeidbar. Das muss man einfach als systemimmanentes Faktum hinnehmen und ist auch grundsätzlich kein Problem. Unangenehm wird es dann, wenn zu viele und zum Teil auch unnötige Abhängigkeiten existieren, so dass die Wartbarkeit des Systems rapide sinkt. Plötzlich haben selbst kleine Änderungen unvorhersehbare Nebeneffekte, neue Fehler schleichen sich ein und das System wird fragil. Der Aufwand für Modifikationen ist nur noch schwer abschätzbar, weil man nicht genau weiß, wie viele Komponenten man "anfassen" und ändern muss. Die Management-Vorgabe lautet daher meistens: „Never touch a running system!“
Zur Veranschaulichung einer sehr starken Abhängigkeit und der damit verbundenen Problematik hier ein kleines Code-Beispiel:
Der Code ist nicht falsch! Objekte der Klasse Example benutzen Objekte vom Typ DatabaseThingie, um Daten aus einer Datenbank zu laden (DatabaseThingie::select) und auch wieder zu persistieren (DatabaseThingie::insert). Dennoch ist dieses Design problembehaftet, denn sobald die Anforderung gestellt wird, das man die Daten auch in einem Filesystem laden/speichern, oder per TCP/IP-Verbindung übertragen können muss, ist die starke Abhängigkeit von Example zu DatabaseThingie sehr störend. Zudem wird das Testen erheblich erschwert, denn was will man tun, wenn man in einer Umgebung testet, in der man nicht über eine Datenbank verfügt und DatabaseThingie somit keine Verbindung aufbauen kann?
Eine Lösung: Dependency Injection
An dieser Stelle kommt nun Dependency Injection ins Spiel! Die Idee ist, das man die Klasse Example nicht unmittelbar abhängig von einer konkreten Implementierung einer Persistenzlösung macht, sondern das man diese Abhängigkeit von aussen in Example "injiziert". Dafür müssen wir zunächst abstrahieren und eine abstrakte Klasse (In Java™: ein Interface) einführen, welche als Basisklasse für alle konkreten Persistenzklassen dienen kann:
Nun wird Example so abgeändert, das Example eine Instanzvariable vom Typ Persistence besitzt, und das man bei der Erzeugung einer Instanz von Example eine Instanz von Persistence als Konstruktor-Parameter mitgibt:
Persistence dient als Basisklasse von z.B. DatabasePersistence:
Bei der Erzeugung von Example wird nun ein Objekt vom Typ DatabasePersistence als Parameter übergeben (sog. Constructor Injection):
DatabasePersistence ist nur eine von vielen, denkbaren, konkreten Persistenz-Klassen. So ist beispielsweise eine Klasse FilePersistence vorstellbar, mit der man eine Instanz von Example in einem Dateisystem persistieren kann. Eine Klasse MockPersistence kann als Platzhalter (Dummy) für eine reale Persitenz-Implementierung dienen und somit die Testbarkeit erhöhen, usw. Dieses UML-Klassendiagramm illustriert einige Möglichkeiten.
Darüber hinaus wäre es auch noch möglich, das Example Setter besitzt um die Abhängigkeit zu injizieren (sog. Setter Injection). Der Vorteil von Setter Injection ist das die Art und Weise, wie Example Daten lädt und/oder persistiert, zur Laufzeit austauschbar wäre!
Die dritte Möglichkeit Abhängigkeiten zu injizieren ist Interface Injection. Dazu wird in C++ eine abstrakte Klasse (In Java™: ein Interface) definiert, um die Injection zu kennzeichnen. Die abhängigen Klassen implementieren die rein virtuellen Methoden dieser Basisklasse.
Wie man sieht, ist Dependency Injection eine effektive Methode, um das Design von Software flexibel zu machen, Abhängigkeiten zu minimieren und die isolierte Testbarkeit von Code zu erleichtern. Objekte lassen sich nun zur Laufzeit quasi "zusammenstecken" und können voneinander auch wieder gelöst und neu konfiguriert werden, was die Bildung von Varianten von ein- und derselben Applikation ermöglicht.
„Dependency Injection is a key element of agile architecture“ (Ward Cunningham)
DI ist eines der zentralen Konzepte in sog. Inversion of Control (IoC) Frameworks, wie z.B. das im Java™-Umfeld sehr populäre Spring-Framework oder PocoCapsule für C++. Doch dazu ein anderes Mal mehr...




