Zum Inhalt springen

Dependency Injection (Teil 4)

Heute kommen wir zum Abschluß des Dependency Injection Themas. Nachdem 3 Konzepte vorgestellt wurde, gibt es heute 3 Frameworks, die diese Konzepte umsetzen. Die sind zum Teil ersetzbar, aber es geht hier auch darum Alternativen aufzuzeigen. Es schließt sich somit der Kreis und die Unklarheiten der Konzeptartikel werden aufgeklärt.

Bevor wir die einzelnen Frameworks angehen gibt es noch eine kleine Anpassung die durchgeführt wurde. Wer die Quelltexte zum selber Spielen herunterlädt wird sich sonst wohl etwas wundern. Die MyDatabase Klasse wurde so angepasst, dass beim Aufruf der query-Methode eine Ausgabe auf die Kommandozeile erfolgt. Da keine echte Logik in der Klasse verbaut wurde, ist dies eine Möglichkeit den Aufruf zu verfolgen. Da es ein PoC ist, wurde ein einfaches echo benutzt. Im Produktivcode kann man dann auch einen schönen Logger einbauen. Weiter gibt die Methode nun true zurück. Dies soll ein erfolgreiches Absetzen des Statements simulieren.

In der Example-Klasse wird entsprechend auf das true reagiert und eine entsprechende Log-Meldung ausgegeben. Hiermit kann man sicherstellen, dass die Kombination der beiden Klassen auch wirklich funktioniert hat. Denn nur dann werden beide Logmeldungen rausgeschrieben. Und noch mal sei erwähnt: Es ist ein PoC.

Nun kommen wir aber zur Implementierung.

Constructor Injection:
Für dieses Konzept wurde Yadif (Yet another Dependency Injection Framework) benutzt. Das Framework ist recht übersichtlich und besteht aus einer Container-Klasse für den DI-Container und einer Exception Klasse. Unsere PHP-Klassen mit dem Beispiel-Code sind bereits aus Teil 1 bekannt und daher müssen wir uns nur die Konfiguration und Benutzung von Yadif anschauen. Diese sieht in PHP so aus:

$configuration = array(
	'Example' => array(
		'class' => 'Example',
		'arguments' => array(
			'__construct' => array('DB')
		)
	),
	'DB' => array('class' => 'MyDatabase')
);

$container = new Yadif_Container($configuration);

$example = $container->getComponent('Example');
$example->getSomething();

Die Konfiguration des Containers ist hier als verschachteltes Array angelegt. Den Container selbst müsste man noch in ein Singleton packen, damit man das eigentliche Problem nicht nur verschiebt sondern löst. Aber hier ist der Vorteil, dass man in einem Testfall den Produktiv-Container nimmt und die Configuration on-the-fly mit einer replace-Methode so anpassen kann, dass die Abhängigkeiten im Testfall durch Mocks ersetzen werden.

Ansonsten sieht man nur noch wie eine Klasse über den Container instaniiert wird und als Beispiel wird die getSomething-Methode aufgerufen, die ja den Zugriff auf die Datenbank simuliert.

Setter Injection:
Das Setter Injection Konzept wurde in Teil 2 dieser Serie erklärt und als Beispielframework schauen wir die Implementierung des Symfony-Frameworks an. Diese ist in den Symfony-Components unabhängig vom Gesamtframework verfügbar. Konfiguriert sieht dies im Code so aus:

$sc = new sfServiceContainerBuilder();

$sc->register("database", "MyDatabase");
$sc->register("example", "Example")->addMethodCall('setDatabase', array(new sfServiceReference('database')));

$example = $sc->example;
$example->getSomething();

Zuerst wird ein Container erzeugt, in diesem wird mit dem Namen „database“ die Klasse MyDatabase verknüpft. Das passiert ebenfalls mit der Example-Klasse. Diese bekommt aber noch die Information, dass beim initialisieren der Klasse die Methode setDatabase aufgerufen werden soll und dort die Klasse, die unter dem Namen database zu finden ist als Parameter dient.

Hier werden die Magic Methods genutzt, um an die initialisierte Klasse hinter example zu gelangen. Und der Aufruf von getSomething ist bereits bekannt.

Interface Injection:
Das Interface Injection Konzept wurde hier ja sehr kontrovers diskutiert und es wurde auch in Frage gestellt, ob das Konzept in der PHP-Welt überhaupt funktionieren kann. Diese Frage hat sich Benjamin Eberlei auch gestellt und ein PoC implementiert. Das hilft mir natürlich ungemein und daher nutze ich seine Implementierung als Bibliothek:

Whitewashing_Container::registerComponent('InjectDBAccess', new MyDatabase());
$example = Whitewashing_Container::load('Example');

$example->getSomething();

Um diese Konstruktion zum Laufen zu bewegen muss man noch eine kleine Anpassung im Code vornehmen. Denn die Methode, die InjectDBAccess nutzt für das Injizieren der Abhängigkeit muss gleich dem Klassen-Namen sein. Das ist aber nur eine Feinheit, die man mit etwas Motivation in der Bibliothek anpassen kann.

Es wird im Container registriert welche Klasse benutzt wird, wenn das entsprechende Interface gefunden wird. D.h. Example muss in unserem Beispiel InjectDBAccess implementieren und darum wird die Klasse MyDatabase injiziert, da das InjectDBAccess Interface mit MyDatabase verknüpft ist.

Anschließend wird eine Instanz der Example-Klasse aus dem Container genommen und wie zuvor getSomething aufgerufen wird.

Man kann hier sehr gut erkennen, dass die Abhängigkeiten der Klassen durch die Interfaces definiert werden und man daher eine übersichtlichere Konfiguration des Containers vorfindet.

Published inAllgemein

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close