HowTo: Dependency Injection & Service Locator

image Vor gut zwei Wochen hat Alexander Groß bei der .NET Usergroup Dresden das Thema Dependency Injection in den Raum geworfen und es auch sehr anschaulich an Beispielen verdeutlicht. Er zeigte den IoC Container Castle Windsor und warum ein ServiceLocator keine so gute Idee ist. Mit dem Blogpost versuche ich mal diese scheinbare Raketenwissenschaft etwas aufzuschlüsseln.

Dependency Injection? Service Locator? WTF?

Wenn man über das Thema sich etwas beließt, dann kommt man früher oder später zu den riesigen Erklärungen von Martin Fowler (ServiceLocator vs. DI). Das ist bestimmt toll beschrieben und ich hab das schon x-mal angefangen, aber bis zu dem .NET Usergroup-Treffen war es mir noch nicht ganz bewusst wie das praktisch denn nun aussieht. Darum versuch ich es mal ganz einfach.

Worum geht es hier eigentlich?

Jede Software hat irgendwelche “Abhängigkeiten”. Sei es eine Datenbank, sei es das Filesystem auf dem zugegriffen wird oder sei es ein interner Dienst oder Service der genutzt wird. Damit man das komplizierte Gebilde später auch vielleicht irgendwie testen kann, empfiehlt es sich auf eine Schnittstelle zu programmieren.
Kleines Szenario: Wir haben ein Formular wo sich User anmelden können. Nach dem anmelden soll eine Mail verschickt werden. Die Daten validieren und verarbeiten macht der “UserService“. Dieser benutzt zum Mails versenden etwas vom Typ “IMailService“.
Jetzt kommt aber genau der interessante Punkt: Woher soll unser UserService wissen, woher er eine Instanz vom Typ “IMailService” bekommen soll?

Variante 1: Service Locator

Achtung: Jetzt folgt Code, wo der eine oder andere mit dem Kopf schüttelt -  der Code ist heute noch so im Einsatz ;)

Die erste Idee wie man solche Abhängigkeiten “kontrollieren” kann wäre wenn man eine Art “Gott-Klasse”/”Setup” hat, in dem man die Komponenten registriert. Mit dieser “Gott-Klasse” könnten wir uns auf Befehl jeden Typen erstellen:

    public class InstanceFactory : IInstanceFactory
    {
        public T GetInstanceOf<T>()
        {
            if (typeof(T) == typeof(IMailService))
            {
                return (T)(object)new EmailService();
            }
        }
    }

An der Stelle könnte man natürlich auch auf eine XML Datei oder irgendwas anderes zugreifen.

Angewendet würde diese Klasse im UserService dann so:

IInstanceFactory factory = new InstanceFactory();
IMailService srv = factory.GetInstanceOf<IMailService>();

Diese “Gott-Klasse” könnte natürlich auch eine statische Klasse sein, aber um den UserService testbar zu halten, können wir über den Konstruktor im UnitTest ein andere InstanceFactory reingeben:

        public UserService()
            : this(new InstanceFactory())
        {
        }

         public UserService(IInstanceFactory factory)
        {
            this._factory = factory;
        }

Im Normalfall wird also unsere Implementation von oben genommen und ansonsten könnten wir auch eine TestInstanceFactory reingeben. Geht soweit auch, ist aber eigentlich nicht so gut wie ich feststellen musste.

Wo liegt beim Service Locator das Problem?

Bei dieser Variante weiß man nicht genau, welche Abhängigkeiten eine Klasse hat. Wenn man die Abhängigkeiten im Konstruktor sieht, dann weiß man, dass z.B. der UserService eine Instanz vom Typ IMailService benötigt. Durch diese “Gott-Klasse” kann man plötzlich kreuz und quer irgendwelche Services aufrufen. Das macht das debugging und testen schwieriger.

Die bessere Methode: Inversion of Control Container

Hier gilt (wie immer) : Solides Halbwissen ist vorhanden – wenn ich hier Quatsch erzähle, dann berichtigt mich ruhig.

Um nicht zu weit auszuschweifen: Um die Abhängigkeiten auf den ersten Blick zu erkennen, ist es meiner Meinung nach gut, wenn diese über den Konstruktor definiert werden. Eine andere Art wäre dies über Properties zu machen. Das ist aber IMHO nicht so einleuchtend wie im Konstruktor.

Rund um das Inversion of Control Thema gibt es bereits eine Vielzahl von Frameworks, die einem dabei helfen:

Auf den ersten Blick ähneln sich die Konzepte vom Service Locator und diesen Frameworks. Bei Den IoC Container legt man immer eine Art “Konfiguration” nach dem Schema: “Wenn du nach Instanz von Typ X gefragt wirst, dann gibt ihm eine Instanz vom Typ X.” an. Allerdings sind die benannten Frameworks hier weitaus cleverer als mein Code von oben.

Wir spinnen das Szenario von oben weiter: Der UserService hat auch ein Interface IUserService und wird im UserController verwendet. Hier mal ein Beispiel mit Castle.Windsor:

   WindsorContainer container = new WindsorContainer(new XmlInterpreter());
   IUserService srv = container.Resolve<IUserService>();

Die “Resolve” Methode ist im Prinzip ähnlich wie das “GetInstanceOf” Methode. Allerdings hat unser UserService wiederrum eine Abhängigkeit auf den EmailService. Das wird allerdings alles vom Framework geregelt und alle Abhängigkeiten werden sauber aufgelöst.

Der Vorteil an der Methode ist, dass man im Unittest einfach so die Klassen benutzen kann und sofort die Abhängigkeiten durch den Konstruktor sieht. Das macht die Sache wesentlich durchschaubarer.

Ich erspare mir hier mal ein konkretes Beispiel, da es im Netz sehr viele gute HowTos zu den einzelnen Frameworks gibt.

Diese Frameworks können natürlich noch wesentlich mehr als pure Instanzen zurückgegeben, aber das würde jetzt zu weit führen. Ich hoffe ich konnte erstmal ein wenig Licht ins Dunkel bringen. Falls ich in diesem Post irgendwelche Sachen aber komplett falsch verstanden habe oder einfach Begrifflichkeiten verwechselt habe, dann korrigiert mich bitte :)


Kick It auf dotnet-kicks.de
Wenn dir der Blogpost gefallen hat, dann hinterlasse doch einen Kommentar. Wenn du auf dem Laufenden bleiben willst, abonniere unseren RSS Feed oder folge uns auf Twitter.

About the author

Written by Robert Mühsig

Robert Mühsig (@robert0muehsig) ist Webentwickler und beschäftigt sich mit Web-Frameworks (vor allem dem ASP.NET MVC Framework) und scheut sich auch nicht vor Javascript. Ansonsten bloggt er über all jene Probleme, die ihm über den Weg laufen. Seit 2008 ist er Microsoft MVP für ASP.NET und er arbeitet bei der T-Systems Multimedia Solutions GmbH in Dresden. Treffen kann man ihn online via Twitter (@robert0muehsig) oder dieser Seite oder bei der .NET User Group Dresden.

6 Responses

  1. Hallo Robert,

    eine sehr schöne Zusammenfassung!

    Alex

    Reply
  2. Hallo Robert,
    schöne Einführung in das Thema. Was hier vielleicht nicht so ganz deutlich geworden ist: Ein weiterer Vorteil von DI und IOC ist, neben der Testbarkeit, die einfache Möglichkeit, Komponenten auszutauschen (Komponentendesign). So ist es z.B. ein Leichtes Serialisierungskomponenten auszutauschen (XML gegen binary or whatever). Wichtig dabei ist nur daß diese Komponenten eine gemeinsame Kommunikationsschnittstelle (Interface) implementieren. Mit diesem Verfahren lassen auch Presentation Layer austauschen. Mit der richtigen Architektur kann man die gleiche Applikation mit einem Web-UI verbinden oder z.B. mit Windows Forms.
    Gruß
    Rainer

    Reply
  3. Hallo Robert,

    es gibt bekanntlich keine dumme Fragen, also riskier´ ich´s mal…

    Die Klasse UserService: IUserService stellt ja einen Konstruktor für Constructor Injection bereit:
    UserService(IInstanceFactory factory) { … }

    Mit IOC-Containern wie Castle Windsor oder LightCore kann man nun über die Konfigurationsdateien “anmelden”, dass der Konstruktor der Klasse UserService (oder bzw. die Klasse als solche) eine Abhängigkeit von IInstanceFactory besitzt. Soweit richtig?

    Ich hätte mir aber folgendes von einem IOC-Container gewünscht: Damit ich mich beim Schreiben der Konfiguration für den IOC-Container eben NICHT MEHR darum kümmern muss, welche Abhängigkeiten die Klasse Kunde per Constructor Injection nennt, soll der IOC-Container selbst erkennen (bspw. über Reflection), dass UserService eine IInstanceFactory benötigt, selbständig prüfen, ob es schon eine Instanz von ILogger gibt, und wenn nicht, selbige aufgrund der Konfiguration eigenständig instanzieren und dann erst den Konstruktor von UserService aufrufen.

    Anders ausgedrückt: Soweit ich das Konzept von LightCore oder CastleWindsor richtig verstanden habe, muss ich mich ja immer noch selbst darum kümmern, dass die Abhängigkeit der Klasse X vom Typ Y (weil als Konstruktorargument erwartet) irgendwo (z.B. in der Konfiguration) “angemeldet” wird.
    Erhofft hätte ich mir eine entsprechende Intelligenz des IOC-Containers; habe ich hier etwas übersehen oder ist das so?

    Herzlichen Dank für Deine Meinung,
    Gruß
    Ron

    Reply
  4. Hallo Ron,
    das hast du genau richtig erkannt. Die IInstanceFactory geht eher in die Richtung “Worst-Practices” weil es quasi ein ServiceLocator ist.
    Dort ist das Problem halt, dass man a) nie genau weiß, was die Klasse überhaupt für Abhängigkeiten hat und b) dass man keine wirkliche Kontrolle hat, weil die Klasse ganz wild Services aufrufen kann.

    Mein unteres Beispiel mit WindsorCastle und dem Aufruf von “container.Resolve();” würde automatisch die Abhängigkeiten aufschlüsseln und hätte die Intelligenz die du ansprichst.

    Dabei sucht WindsorCastle automatisch nach dem Konstruktor, welcher die meisten Abhängigkeiten entgegennimmt und schlüsselt diese dann immer weiter auf.

    Reply

Comment on this post

Letzte Posts

  • Carriage Return / Neue Zeile in Textareas

    Eine kleine Aufgabe: Jede neue Textzeile (Carriage Return/Wenn man Enter drückt ) in einer Textarea soll ein Element in einer Auflistung sein – wie mach ich das jetzt am einfachsten? Eigentlich ein grundlegendes Element im Web und der Nutzer macht bewusst Absätze – daher wäre es nur gerecht, wenn man das auch entsprechend würdigt. Kleine ...

  • image.png
    Doom, Quake, Wolfenstein & co. Source Code auf GitHub

    id Software, die Macher von Doom, Quake, Wolfenstein & co., stellen regelmäßig ihre älteren Spieltitle als Open Source zur Verfügung. Das Ganze runterzuladen fand ich bisher immer recht mühselig, allerdings gibt es seit kurzer Zeit die Sourcen auch auf GitHub. Darunter Spiele wie Doom 3, Quake 3, Wolfenstein für iOS. Wer also schon immer mal ...

  • image.png
    Twitter Bootstrap 2.0 released & “Release Präsentation”

    Wie bereits vom Twitter Bootstrap Team angekündigt wurde offiziel die Version 2.0 des UI Toolskits “Twitter Bootstrap” veröffentlich. Zudem wurden die Slides, welche bei der Release Party gezeigt wurden auch veröffentlicht: Downloads finden sich auf der Twitter Bootstrap Seite auf GitHub. Wenn dir der Blogpost gefallen hat, dann hinterlasse doch einen Kommentar. Wenn du auf ...

  • image.png
    Javascript zu Dart Translator

    Dart, Google Javascript Alternative, wurde vor ein paar Monaten vorgestellt und die Webentwickler Szene ist noch etwas gespalten, ob Dart nun überflüssig ist oder einfach nur cool und längst überfällig ist. Um die Sprache näher zu erläutern hat Google die grundlegenden Javascript Basics nach Dart übersetzt. Das Ergebnis ist der “Translator”. Der Name mag momentan ...

  • Twitter Bootstrap 2.0–“Beta”

    Twitter Bootstrap, ein UI-Toolkit für Web-Applikationen von Twitter, erscheint (wie bereits berichtet) demnächst in der Version 2.0. Der offizielle Release ist am 31. Januar, allerdings beginnt jetzt laut Mark Otto (einer der Hauptentwickler von Twitter Bootstrap) die intensive Test-Phase. Das heisst, das es nun offiziel auch die 2.0 Dokumentation online gibt. Im Vergleich zur aktuellen ...

Support us!

Facebook