Bryan Albrecht

Dependency Injection

In diesem Tutorial werde ich erklären, für was Dependency Injection da ist und wie man es einsetzt.

Ich werde ein Codebeispiel verlinken, damit man auch die praktische Umsetzung studieren kann. Dieses Beispiel wird in C# mit dem Framework NInject umgesetzt.

Definition

Dependency Injection ist dafür da, um die Bindung von hart codierten Abhängigkeiten zwischen Klassen zu lösen. Das heisst, dass das gesamte Programm abstrakter aber auch einfacher testbar wird.

Eine Art von Dependency Injection ist, zum Beispiel, dass im Konstruktor eine Klasse nur ein Interface als Parameter verlangt wird. Ebenfalls wird an einer zentralen Stelle festgelegt, welche Klasse aufgerufen werden soll, wenn ein bestimmtes Interface verlangt wird. Das heisst, dass diese Klasse leicht durch eine andere Klasse mit den gleichen Schnittstellen ausgetauscht werden kann.

Aufbau ohne Dependency Injection

Wenn der Programmcode ohne Dependency Injection aufgebaut wird, dann werden die Abhängigkeiten etwa so aussehen:

ohne

In dieser Grafik wird gezeigt, dass die aufrufende Klasse eine feste Beziehung zur Klasse hat, welche vom gewünschten Inteface erbt. Wenn die erbende Klasse ausgetauscht werden soll, so muss dies bei allen Implementierungen geändert werden. Ebenfalls kann es sein, dass man den Sinn hinter den Interfaces nicht sieht, da diese "nur" ein grösseren Zeitaufwand mit sich bringen.

Aufbau mit Dependency Injection

Wenn ein Code mit Dependency Injection aufgebaut wurde, dann ist dieser viel abstrakter als normal. Dies kann am Anfang etwas verwirrend sein, weil man die Logik dahinter nicht auf anhiebt versteht.

Umgesetzt kann die Beziehung zwischen Klassen etwa so aussehen:

ohne

Die Box auf der rechten Seite ersetzt die hart Codierte Beziehung zwischen Klassen. Das heisst, dass im Container festgelegt wird, welche Klasse verwendet werden soll, wenn ein bestimmtes Interface gefragt wird.

Aus diesem Grund wird in der User-Klasse nur mit dem Interface gearbeitet.

Diese Methode hat den Vorteil, dass die Klassen leicht durch Dummy-Daten ausgetauscht werden können.

Beispiel

Im Folgenden Beispiel wird die Main Funktion eines Programmes gezeigt:

ohne

Die auskommentiert Codezeile ist fast identisch mit der, welche direkt darunter liegt.

Der einzige Unterschied liegt in der Klasse, welche verwendet wird.

Wenn das Programm nun ausgeführt wird, so würde die Klasse ConsoleText verwendet werden.

Wenn die obere CodeZeile verwendet werden würde, so würde die Klasse TestConsoleText verwendet werden.

Diese Bindings sind die Aufgaben des Containers.

Das gesammte Beispiel findet man hier: Github.com

Vor- und Nachteile

Vorteile:

  1. Weniger Abhängigkeiten
  2. Höhere Wiederverwendbarkeit
  3. Höhere Testbarkeit
  4. Höhere Flexibibiltät
  5. Schöner Code
  6. Kein «Dependency Carrying»

Nachteile:

  1. Grösserer Zeitaufwand
  2. Viel abstrakterer/komplizierterer Code
  3. Schwere Einführung für neue MA

Links

Martinfowler.com