Peter Bucher - Mein Experiment, meine Spielereien, meine Welt...   ·   Stefan Falz   ·   Jürgen Gutsch   ·   Golo Roden   ·   ASP.NET Zone   ·   Microsoft ASP.NET
Willkommen bei ASP.NET Zone. Anmelden | Registrieren | Hilfe

Teil 1: Umkehrung der Kontrolle erklärt, oder: Von der Fabrikmethode zum DI-Container

Dies ist der Beginn der Artikelserie "Umkehrung der Kontrolle erkärt, oder: Von der Fabrikmethode zum DI-Container" mit folgenden Teilen:

LightCore gibt es schon seit längerer Zeit, andere DI-Container noch länger.
In dieser Blogpostreihe geht es aber nicht primär um LightCore, auch nicht um die anderen DI-Container.

Ich möchte mit diesem Blogpost versuchen zu erkären, wie denn die ganze Geschichte funktioniert.

Vom Ursprung bis zu einer implementierten Komponente.

Das Feedback zur LightCore-Webseite und LightCore selber hat auf alle Richtungen ausgeschlagen.

Von: Wow, cool. Brauche ich!, zu: Was ist das denn?, bis zu: Braucht man sowas?, über: Sowas braucht man nicht!, bis zu: Mache ich von Hand oder in 10 Zeilen!.

An dieser Stelle sei gesagt: Ich würde die Fragen nach dem "brauchen" in jedem Fall mit "Ja" beantworten, je nach Kontext wo denn etwas eingesetzt werden soll.

Die Grundprobleme:

  1. Abhängigkeit zu konkreten Typen
  2. Redundanz bei der Konstruktion von Typen jeglicher Art
  3. Keine einfache Möglichkeit der Austauschbarkeit von Typen
  4. Keine einfache Möglichkeit der Erweiterbarkeit über Typen

Wieso überhaupt "Abhängigkeit lösen"?

Um die Probleme 2, 3 bis 4 zu lösen. Das heisst, die möglichen Redundanzen - also bspw. doppelten Instanziierungen von Klassen - zu vermeiden. Durch das Lösen der Abhänigigkeit von einem konkreten zu einem abstrakten Typen können die Typen ausgetauscht werden und eine Erweiterung ist mit weniger Aufwand möglich.

Die Abhängigkeit wird nicht "verbannt", sondern gelöst. Lösen heisst in diesem Fall, dass die Abhängigkeit nicht mehr auf _einen_  _konkreten_ Typen besteht, sondern auf _eine_ Abstraktion, die _mehrere_ unterschiedliche Implementationen beinhalten kann.

Anstelle dass nur "World" genutzt werden kann, kann dann mithilfe der Abstraktion "WorldBase", "SecondWorld", "IrgendwasWorld", etc... benutzt werden.

Diese Grundprobleme können auf verschiedene Arten gelöst werden. Vorerst die einfachsten Möglichkeiten.

Eine einfache Abhängigkeit zu konkreten Typen kann folgendermassen aussehen:

World world = new World();
world.SayHello();

 

In diesem Beispiel ist die Methode "SayHello" von der konkreten Klasse "World" abhängig.

Wollen wir diese Abhängigkeit lockern / lösen, brauchen wir eine Abstraktion irgend einer Art.
Das kann ein Interface sein, eine Abstrakte Klasse oder auch eine einfache konkrete Oberklasse.

Am Beispiel einer abstrakten Klasse:

WorldBase world = new World();
world.SayHello();

 

Ja, das war erst der erste Schritt. Das Problem besteht immer noch, es liegt in "new World();". Wir referenzieren den Konstruktur der konkreten Klasse.
Die einfachste Lösung hierfür ist eine Fabrikmethode (Factory Method), womit die Konstruktion der Instanz ausgelagert wird.

Dies könnte so aussehen:

public static WorldBase ConstructMyWorld()
{
                return new World();
}

WorldBase world = ConstructMyWorld();
world.SayHello();

 

So ist das Problem auf einfachste Art gelöst.

Zusammenfassung:
Das Problem war die Abhängigkeit auf die konkrete Klasse "World".
Wenn wir anstelle von der Klasse "World", "SecondWorld" benutzen wollen, brauchen wir das jetzt nur noch an der Stelle "return new World" zu ändern, in "return new SecondWorld();"

Wie häufig wir die Fabrikmethode "ConstructMyWorld" benutzen, bleibt uns überlassen.
Die Stelle bleibt, ein Ort der Änderung.

Das wird möglich indem wir eine Abstraktion benutzen, in unserem Falle "WorldBase".
Die konkreten Klassen "World", "SecondWorld" müssen von aussen gleich aussehen (Implementieren die gleiche Abstraktion), können allerdings anders implementiert sein.

Die Angabe des Typs "WorldBase" braucht bei der Zuweisung am Ort des Benutzens angegeben werden, sowie auch als Rückgabetyp der Fabrikmethode.

Das war die einfachste Möglichkeit der Umkehrung per Fabrikmethode.

Die Frage bleibt evt. noch: Wieso Umkehrung?

Am Anfang hatte die Zuweisung am Ort des Benutzens die Kontrolle über den konkreten Typen, sie gab also an, was sie möchte.

Jetzt liegt die Kontrolle _in_ der Fabrikmethode. Der Ort des Benutzens weiss nur, das da ein Typ zurückkommt, der aussieht wie "WorldBase", nicht mehr und nicht weniger.

Am Schluss noch der vollständige (vermutlich nicht sofort lauffähige = Pseudo) Code:

public class ConsoleApplication
{
                public static void Main()
                {
                                WorldBase world = ConstructMyWorld();
                                world.SayHello();
                }

                public static WorldBase ConstructMyWorld()
                {
                                return new World();
                }
}

public abstract class WorldBase
{
                public abstract void SayHello();
}

public class World : WorldBase
{
                public void SayHello()
                {
                               Console.WriteLine("Hello World");
                }
}

public class SecondWorld : WorldBase
{
                public void SayHello()
                {
                               Console.WriteLine("Hello SecondWorld");
                }
}

Veröffentlicht Sonntag, 5. Dezember 2010 14:47 von Peter Bucher

Kommentare

# re: Teil 1: Umkehrung der Kontrolle erkärt, oder: Von der Fabrikmethode zum DI-Container

Hallo Peter,

dieses Thema einmal von der Pieke an aufzurollen, finde ich eine exzellente Idee. Ich freue mich schon auf die weiteren Teile.

Gruß

Rainer Hilmer

Sonntag, 5. Dezember 2010 21:59 by Rainer Hilmer

# Dependency Injection und ASP.NET MVC 4 Autofac

In annähernd jedem Projekt nutze ich seit 2008 Dependency Injection (DI) um Komponenten und Module lose

Dienstag, 5. Februar 2013 09:55 by Jürgen Gutsch

# re: Teil 1: Umkehrung der Kontrolle erklärt, oder: Von der Fabrikmethode zum DI-Container

Am Beispiel einer abstrakten Klasse:

WorldBase world = new World();

world.SayHello();

Geht das denn überhaupt (bin neu bei siSharp)

Eine abstrakte Klasse und davon dann eine neu Instanz bilden?

Grüße,

DUNE

Montag, 6. Oktober 2014 21:02 by dune
Anonyme Kommentare sind nicht zugelassen