Was ist eigentlich… Dependency Injection (DI)?

Update: Zum Thema Code Sharing und Dependency Injection gibt es jetzt auch einen kostenlosen Online Kurs in der MVA [LINK].

 

 

In den vergangenen Tagen und Wochen war ich häufig auf Konferenzen unterwegs und natürlich auch als Zuschauer in der ein oder anderen Session. Dabei ist mir aufgefallen, dass “Dependency Injection” offenbar noch nicht so bekannt ist, wie ich es erwartet hätte. In verschiedenen Vorträgen haben Speaker die Audienz gefragt: “Wer weiß, was Dependency Injection ist?”. In jedem Fall gingen maximal 5% der Hände nach oben. Selbst unter der Annahme, dass einige der Teilnehmer einfach keine Lust hatten, die Hand zu heben, ist das ein sehr geringer Anteil.

Mit diesem Blogpost möchte ich DI erklären und damit dafür sorgen, dass DI bekannter wird. DI ist nämlich keinesfalls einer dieser sagenumwobenen Bullshitbingo-Begriffe, die einem als Entwickler aus dem Architekten-Elfenbeinturm um die Ohren gehauen werden, mit den Worten “Thy shall use DI”. Es ist eine wirksame, eigentlich schon fast unspektakuläre Methode, den Codeaufbau und das Componentendesign zu verbessern. Nur der Name ist etwas komisch und sorgt vielleicht für Verwirrung… aber der Reihen nach.

Es gibt unendlich viele Erklärungen im Internet, was DI nun eigentlich ist. (Schaut mal hier auf Wiki…) Das Problem dabei ist, dass man Patterns wie DI eigentlich nur anhand von Code so richtig schön erklären kann. Oder man versucht wie wild außenherum zu erklären, so dass zwar jeder theoretisch versteht, wie es funktioniert, aber dennoch keine Ahnung hat, wie die Implementierung aussieht. Ich wähle hier in diesem Blogpost eine Metapher, die ich so noch nirgendwo gefunden habe, die mir aber sehr passend scheint und die ich in einem der seltenen Momente der geistigen Erleuchtung ins Leben gerufen habe.

Vorneweg: DI ist ein Entwurfsmuster, um ungewünschte Abhängigkeiten zwischen einzelnen Klassen zu vermeiden. Das war sie, die theoretische Erklärung. Füttern wir das ganze mit Leben.

Die Metapher mit dem Meister und dem Lehrling

Ich denke, die folgende Metapher erklärt DI ganz gut.

Stellen wir uns einen Handwerksbetrieb vor, z.B. eine Gärtnerei. In der Gärtnerei gibt es einen Meister und einen Lehrling. Der Lehrling tut, was der Meister sagt. Wenn der Meister zum Lehrling sagt: “Grab ein Loch”, dann fängt der Lehrling an zu graben. Allerdings benötigt er dafür ein Werkzeug, eine Schaufel nämlich. Der Lehrling wird sich also seine Schaufel kaufen. Vielleicht geht er dafür in den Obi, kauft dort eine Schaufel. Vielleicht hat er eh schon eine. Vielleicht nimmt er auch eine, die eh grad rumsteht. In jedem Fall hat der Meister keinen Einfluss darauf, welche Schaufel der Lehrling nimmt.

Der Lehrling selbst hat aber eine Verantwortung, um die er sich eigentlich vielleicht gar nicht kümmern möchte. Er muss nämlich plötzlich dafür sorgen, dass er eine Schaufel herkriegt. Dafür wurde er eigentlich nicht eingestellt, er ist ja nicht der Werkzeugbeschaffer, sondern der Lehrling für Gartenarbeit und er will eigentlich nur in aller Ruhe graben. Es gibt also ein Beziehung zwischen Lehrling und Schaufel, die der Lehrling gar nicht will. Der Meister auch nicht. Eigentlich will diese Beziehung niemand.

Visualisiert sieht das so aus (links der Meister, rechts der Lehrling, ganz rechts die Schaufel):

image

Natürlich kann man das jetzt in Code übersetzen. Der Lehrling hat eine Methode GrabeLoch(). Außerdem hätte er die Möglichkeit sich im Konstruktor eine Schaufel zu beschaffen.

image

Was ist jetzt der Haken? Der Haken ist, dass wir zwischen Lehrling und Schaufel eine Beziehung haben, die wir nicht wollen. Der Lehrling soll eigentlich nur graben, nicht bestimmen, welche Schaufel verwendet werden soll.

Besser wäre es, wenn die Schaufel von “außen” übergeben wird, also vom Meister bestimmt wird, welche Schaufel zu verwenden ist, was so zu visualisieren wäre.

image

 

Der Meister drückt dem Lehrling also eine Schaufel in die Hand und sagt: Hier, grab mal damit ein Loch! Im Code könnte das so aussehen:

image

Die Frage ist jetzt, wie die Implementierung von GrabeLoch() aussieht. Eventuell so:

image

Wenn das der Fall ist, wird die Schaufel dem Lehrling übergeben, der Meister bestimmt also, wo die Schaufel herkommt. Der Lehrling kann aber im Prinzip nur mit einer Art von Schaufel umgehen, nämlich mit Werkzeugen, die von der  Klasse “Schaufel” sind oder zumindest davon abgeleitet sind. Andere Werkzeuge “kennt” er nicht.

Im Endeffekt hat man also nicht so wahnsinnig viel gewonnen, denn der Meister als auch der Lehrling haben jetzt eine Abhängigkeit zur Schaufel. Der Meister muss sie beschaffen, der Lehrling muss in der Lage sein, sie zu verwenden.

Wie wäre es, wenn wir dem Lehrling sagen, er soll einfach mit irgendeiner Ausprägung eines Grabewerkzeug graben können? Wenn wir ihm nicht sagen, ob er einen Spaten oder eine Schaufel oder einen Bagger bekommt. Er soll einfach mit derartigen Grabewerkzeugen umgehen können! Wir können also ein Interface IGrabable Smiley definieren, das einfach nur zum Ausdruck bringt, dass man ein Werkzeug, das dieses Interface implementiert, verwenden kann, um Löcher zu buddeln.

image

Die Schaufel wiederum implementiert dann logischerweise dieses Interface:

image
Und jetzt kommt’s: Der Lehrling ist clever genug nicht nur mit einer Schaufel arbeiten zu können, sondern mit einem beliebigen Werkzeug, das zum Graben verwendet werden kann und daher IGrabable ist. Im Code spiegelt sich das wie folgt:

image

image

Der Meister wiederum hat die freie Auswahl und kann dem Lehrling Schaufel, Spaten oder Bagger in die Hand drücken, um zu seinem Loch zu kommen.

image

image

Was passiert also? Der Meister nimmt irgendein geeignetes Werkzeug (je nach Verfügbarkeit) und der Lehrling bekommt erst mit, welches er verwenden soll, wenn er es tatsächlich verwenden will. Oder anders ausgedrückt:

Die konkrete Implementierung des Werkzeugs ist dem Lehrling egal. Er muss nur damit graben können. Der Meister wird eine Möglichkeit finden, ihm das Werkzeug rechtzeitig zur Verfügung zu stellen.
Das Abhängigkeitsverhältnis zu konkreten Implementierungen ändert sich wie folgt: Der Meister hat eine Abhängigkeit zum Schaufel und zum Lehrling. Der Lehrling hat keine Abhängigkeiten zu konkreten Implementierungen.

image

So erklärt sich auch der Name “Dependency Injection”: Die Abhängigkeiten werden zur Laufzeit dynamisch bekannt gemacht oder injiziert, je nach konkreter Implementierung.

 

Fazit

Wir fassen nochmal zusammen: DI bedeutet eigentlich nichts anderes, als Abhängigkeiten zu konkreten Implementierungen aufzulösen indem man z.B. mit Interfaces arbeitet. Das sorgt dafür, dass die aufrufende Komponente oder Klasse die Abhängigkeiten an die aufgerufene Komponente oder Klasse zur Laufzeit übergibt.

Bleibt noch die Frage, wer das eigentlich braucht. Das einfachste Beispiel ist der Fall von Applikationen, die auf unterschiedlichen Plattformen laufen sollen, die für bestimmte Systemaufrufe unterschiedliche APIs anbieten. Wer hier mit DI arbeitet, der kann plattformspezifisches Verhalten wegabstrahieren und so sehr viel gemeinsamen Quellcode nutzen. Plattformspezifika werden dann zur Laufzeit via DI eingefügt.

Konkretes Beispiel ist das Arbeiten mit den Kartendiensten auf Windows Phone 8.0, Windows Store Apps und Android (via Xamarin). Hier gibt es je Plattform spezifische Implementierungen. Dennoch kann man viel vom restlichen C#-Code teilen.

Um DI zu ermöglichen, wird das Klassendesign innerhalb einer Lösung vermutlich verändert. Damit man hier als Entwickler ein bisschen mehr Komfort hat, kann man auf Frameworks zurückgreifen, die einem an der ein oder anderen Stelle das Leben erleichtern. Es gibt hier unterschiedlichste DI-Container frei verfügbar, ich möchte diesen Artikel aber möglichst einfach halten, daher gehe ich hierauf nicht näher ein.

Ich hoffe, es ist mir gelungen mit diesem kleinen Beispiel den mystischen Nebel rund um DI etwas zu lüften. Über Anregungen und Feedback in den Kommentaren freue ich mich natürlich wie immer.