Dependency Injection: Microkernel selbst gebaut
Ich interessiere mich bereits seit längerer Zeit für CCD.
Eine der CCD-Praktiken
stellt Dependency Injection dar, wobei es im Wesentlichen um nichts anderes, als
das späte Binden von Abhängigkeiten geht, sodass ein Austauschen von Komponenten
ohne ein erneutes Übersetzen der Anwendung möglich wird. Hier stelle ich meine
persönliche Microkernel-Implementierung zur Verfügung, mit der Komponenten per
Konfiguration in eine Anwendung integriert werden können.
Eine ausführliche Zusammenfassung zum Thema Contract-First-Design, sowie eine
(aus der Fachpresse) bekannte Microkernel-Implementierung sind unter: http://weblogs.asp.net/ralfw/
zu finden.
Um den Microkernel einsetzen zu können, wird ein entsprechender Konfigurationsabschnitt
in der app- bzw. web.config-Datei benötigt. Im microkernelSection-Abschnitt befindet
sich eine Auflistung von sogenannten Containern, wobei je Interface jeweils
ein Container definiert wird, dem dann ein (oder mehrere) Implementierungen
hinzugefügt werden können.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="microkernelSection" type="Configuration.MicrokernelConfigurationSection, Configuration"/>
</configSections>
<microkernelSection>
<containers>
<clear />
<container interfaceType="Interfaces.ILogger,Interfaces" defaultComponent="httpLogger"></container>
</containers>
</microkernelSection>
</configuration>
Für das Beispiel des ILogger-Interface habe ich verschiedene Implementierungen im
microkernel-Konfigurationsabschnitt deklariert. Das defaultComponent-Attribut des
container-Elements kennzeichnet die zu verwendende Standard-Implementierung.
...
<container interfaceType="Interfaces.ILogger,Interfaces" defaultComponent="httpLogger">
<components>
<clear />
<component name="httpLogger" implementationType="Logging.HttpLogger, Logger.Http" singleton="true" />
<component name="sqlLogger" implementationType="Logging.SqlServerLogger, Logger.SqlServer" singleton="true" />
<component name="consoleLogger" implementationType="ConsoleLogger, DebugUtils" singleton="true" />
</components>
</container> ...
Im Anwendungsverzeichnis befinden sich alle Abhängigkeiten - wobei demo.exe keine
Verweise auf Assemblies enthält, die Implementierungen bereitstellen.

Konkrete Implementierungen zur Laufzeit einbinden
Wie die Anwendung konfiguriert werden muss, habe ich im vorherigen Abschnitt bereits gezeigt.
Bevor ich auf die Implementierung der DynamicBinder-Klasse eingehe, zeige ich jedoch
zuvor deren Verwendung. Das folgende Beispiel zeigt, wie ein ILogger via Microkernel
in der Anwendung erzeugt werden kann...
using Interfaces;
private sealed class Program
{
void Main(string[] args)
{
ILogger logger = DynamicBinder.CreateInstance<ILogger>();
logger.Log("Hello World.");
}
}
Natürlich kann auch eine von der Standard-Implementierung abweichende Komponente zur Laufzeit
angezogen werden. Dazu kann der CreateInstance-Methode einfach ein Schlüssel übergeben werden, unter
dem die Implementierung in der Konfiguration hinterlegt ist.
...
ILogger sqlLogger = DynamicBinder.CreateInstance<ILogger>("sqlLogger");
logger.Log("Hello World."); ...
Die DynamicBinder-Klasse
Das Herzstück der DynamicBinder-Klasse bildet die CreateInstance()-Methode; darüber hinaus
hat die Klasse nicht viel zu bieten, denn mehr ist für einen funktionierenden Mini-Kernel
auch nicht notwenig :-)
Anhand des Typs, der in T übergeben wird, wird der Container ermittelt, der die
Komponente beinhaltet, von der eine Instanz erzeugt werden soll. Wird im name-Parameter
nichts angegeben, wird die in defaultComponent festgelegte Implementierung angezogen.
...
public static T CreateInstance<T>(string name)
{
if (configSection == null)
throw new InvalidOperationException();
Configuration.ContainerConfigurationElement container;
if ((container = configSection.Containers.GetContainer( typeof(T))) != null)
{
if (string.IsNullOrEmpty(name))
name = container.DefaultComponent;
Configuration.ComponentConfigurationElement component;
if ((component = container.Components[name]) != null)
{
Type impl = Type.GetType(component.Implementation);
if ((component.IsSingleton) && (singletons.ContainsKey(impl)))
return (T) singletons[impl];
T instance;
Dictionary<string, string> settings;
if ((settings = component.GetSettings()) != null)
instance = (T) Activator.CreateInstance(impl, settings);
else
{
instance = (T) Activator.CreateInstance(impl);
}
if (instance != null)
{
if ((component.IsSingleton) && (singletons.ContainsKey(impl) == false))
singletons.Add(impl, instance);
return instance;
}
}
}
return default(T);
} ...
Unterstützung von Singletons
Der Microkernel unterstützt die Verwendung von Singletons. Nach dem Erzeugen einer Objekt-Instanz
wird diese zwischengespeichert, wenn die Komponente in der Konfiguration als Singleton gekennteichnet
wurde. Wird die Komponente ein weiteres Mal über den Microkernel angefordert, wird keine neue Instanz
erzeugt, sondern das gespeicherte Objekt zurückgeliefert.
...
<container interfaceType="Interfaces.ILogger,Interfaces" defaultComponent="httpLogger">
<components>
<clear />
<component name="httpLogger" implementationType="Logging.HttpLogger, Logger.Http" singleton="true" />
</components>
</container> ...
Speichern der Instanz...
...
if (instance != null)
{
if ((component.IsSingleton) && (singletons.ContainsKey(impl) == false))
singletons.Add(impl, instance);
}
...
Abrufen der Instanz...
...
Type impl = Type.GetType(component.Implementation);
if ((component.IsSingleton) && (singletons.ContainsKey(impl)))
return (T) singletons[impl];
...
Links
Clean Code Developer
http://www.cleancodedeveloper.de/
Download: matzef.microkernel.zip
http://matze-friedrich.de/dev2/downloads/matzef.Microkernel.zip
Visual Studio 2008 Projektmappe
Ralf Westphal: http://weblogs.asp.net/ralfw/
http://weblogs.asp.net/ralfw/archive/2006/08/02/Dynamic-component-binding-made-easier-_2D00_-An-easy-to-use-Microkernel-to-help-reap-Contract_2D00_First_2D00_Design-benefits-in-.NET-programs.aspx
Dynamic component binding made easier - An easy to use Microkernel to help reap Contract-First-Design benefits in .NET programs