SlideShare a Scribd company logo
1 of 50
Dependency Injection 
Eine Einführung mit grundlegenden Beispielen 
(c) Carsten Hetzel, 2014
Was ist eigentlich das 
Problem? 
(c) Carsten Hetzel, 2014
Was ist eigentlich das Problem? 
 Wie üblich bei gängigen Prinzipien aus der 
Softwareentwicklung liegt dem Prinzip ein wiederkehrendes 
Problem zugrunde. 
 Schauen wir uns dazu folgende Klasse an: 
3 (c) Carsten Hetzel, 2014
class UglyCoupling 
{ 
private $handle; 
public function __construct() 
{ 
$filename = 'output.txt'; 
$handle = @fopen($filename, 'w'); 
if (!$handle) { 
throw new RuntimeException('Unable to open output file!'); 
} 
$this->handle = $handle; 
} 
public function updateOrder($orderId, $newValue) 
{ 
// ... update the order 
$output = 'The value of order ' . $orderId . ' has been changed to ' . $newValue . '!'; 
$bytes = fwrite($this->handle, $output); 
if ($bytes === false) { 
throw new RuntimeException('Unable to write to output file!'); 
} 
} 
} 
4 (c) Carsten Hetzel, 2014
Welche Probleme hat 
diese Klasse? 
(c) Carsten Hetzel, 2014
Welche Probleme hat diese Klasse? 
 Der Dateiname ist nicht änderbar 
 Es ist nicht klar, wo im Dateisystem die Datei ggf. erzeugt wird 
 Die Klasse UglyCoupling kann nicht genutzt werden, wenn die Datei nicht 
geschrieben werden kann 
 Die Klasse wirft Exceptions bei Fehlern, die mit ihrer eigentlichen Funktion 
nichts zu tun haben 
 Wenn weitere Dinge beim Aufruf von "updateOrder()" passieren sollen, 
müssen sie in "UglyCoupling" hinzugefügt werden 
 Tests erzeugen Dateien im Dateisystem, auch wenn das auf dem 
Testsystem vielleicht gar nicht gewünscht ist 
6 (c) Carsten Hetzel, 2014
Was ist eigentlich das Problem? 
 Die Datei wird nicht geschlossen (bzw. erst, wenn der PHP-Prozess 
endet) 
 Jede Instanz der Klasse UglyCoupling öffnet die selbe Datei 
 Wenn der Inhalt des "outputs" oder das Ausgabeformat von 
Text auf PDF geändert werden soll, muss die Klasse 
UglyCoupling angepasst werden 
 ... 
7 (c) Carsten Hetzel, 2014
Was ist Dependency 
Injection? 
(c) Carsten Hetzel, 2014
Was ist Dependency Injection? 
 Dependency Injection fordert uns auf Ressourcen 
anzufordern, statt sie selber bereit zu stellen. Es gibt drei 
Wege Ressourcen anzufordern: 
 1. Constructor Injection 
 2. Setter Injection 
 3. Interface Injection 
9 (c) Carsten Hetzel, 2014
Constructor Injection 
 Klassen, die Ressourcen für ihre Arbeit benötigen, fordern 
diese Ressourcen über ihren Konstruktor an. 
 Damit ist gewährleistet, dass die Klasse von Beginn an über 
die Ressourcen verfügt, die sie benötigt. 
10 (c) Carsten Hetzel, 2014
Constructor Injection 
class ConstructorInjection 
{ 
/** 
* @var Service 
*/ 
private $service; 
public function __construct(Service $service) 
{ 
$this->service = $service; 
} 
} 
11 (c) Carsten Hetzel, 2014
Constructor Injection 
 Ein klarer Nachteil der Constructor Injection ist, dass sehr 
früh im Application Lifecycle Ressourcen bereitgestellt 
werden, die evtl. gar nicht zum Einsatz kommen. 
 Eine Konsequenz aus Constructor Injection ist, Ressourcen so 
kostengünstig wie möglich zu erstellen, damit keine 
überflüssige Arbeiten vorgenommen werden. 
12 (c) Carsten Hetzel, 2014
Setter Injection 
 Benötigte Ressourcen werden über Set-Methoden 
bereitgestellt. Diese Methoden können ggf. deutlich nach der 
Erstellung des Anfordernden Objekts aufgerufen und dem 
Klienten bereitgestellt werden. 
 Dabei besteht aber auch das Risiko einen ungültigen 
Zustand, ein unvorhersehbares Verhalten oder sogar einen 
Fehler hervorzurufen. 
 Ggf. muss also die Verfügbarkeit der Ressourcen geprüft 
werden! 
13 (c) Carsten Hetzel, 2014
Setter Injection 
class SetterInjection 
{ 
/** 
* @var Service 
*/ 
private $service; 
/** 
* @param Service $service 
*/ 
public function setService(Service $service) 
{ 
$this->service = $service; 
} 
} 
14 (c) Carsten Hetzel, 2014
Interface Injection 
 Benötigt eine Klasse eine bestimmte Ressource, dann 
implementiert sie eine Interface, welches das Injizieren dieser 
Ressource anfordert. 
 D.h. das Interface fordert die Implementierung z.B. einer 
"inject"-Methode, die als Parameter die entsprechende 
Ressource erwartet. 
15 (c) Carsten Hetzel, 2014
Interface Injection 
class InterfaceInjection implements InjectSercvice 
{ 
/** 
* @var Service 
*/ 
private $service; 
public function injectService(Service $service) 
{ 
$this->service = $service; 
} 
} 
16 (c) Carsten Hetzel, 2014
Was ist eigentlich mit 
einer "Ressource" 
gemeint? 
(c) Carsten Hetzel, 2014
Was sind „Ressourcen“? 
 Eine Ressource kann eigentlich alles mögliche sein - von einer 
einfachen Zahl bis zu einer komplexen Service-Klasse. 
 Es ist also keines Falls so, dass nur Services über Dependency 
Injection angefordert werden sollen. 
 Ein klassisches Beispiel sind Parameter für eine 
Datenbankverbindung. 
18 (c) Carsten Hetzel, 2014
Aufgabe: Überarbeiten sie 
"UglyCoupling“ 
(c) Carsten Hetzel, 2014
Aufgabe: „UglyCoupling“ 
 Überarbeiten Sie die Klasse "UglyCoupling" so, dass die 
benötigten Ressourcen angefordert werden. 
 Bitte führen Sie diese Aufgabe in Teams durch und 
diskutieren Sie Ihre Ansätze. 
 Sie haben 5 Minuten Zeit! 
20 (c) Carsten Hetzel, 2014
class UglyCoupling 
{ 
private $handle; 
public function __construct() { 
$filename = 'output.txt'; 
$handle = @fopen($filename, 'w'); 
if (!$handle) { 
throw new RuntimeException('Unable to open output file!'); 
} 
$this->handle = $handle; 
} 
public function updateOrder($orderId, $newValue) { 
// ... update the order 
$output = 'Order ' . $orderId . ' has been changed to ' . $newValue . '!'; 
$bytes = fwrite($this->handle, $output); 
if ($bytes === false) { 
throw new RuntimeException('Unable to write to output file!'); 
} 
} 
} 
21 (c) Carsten Hetzel, 2014
Lösungen? 
(c) Carsten Hetzel, 2014
Ist das besser? 
Bitte diskutieren Sie folgende Lösung! 
(c) Carsten Hetzel, 2014
class LessUglyCoupling 
{ 
private $fileObject; 
public function __construct(SplFileObject $fileObject) 
{ 
$this->fileObject = $fileObject; 
} 
public function updateOrder($orderId, $newValue) 
{ 
// ... update the order 
$output = 'Order ' . $orderId . ' has been changed to ' . $newValue . '!'; 
$bytes = $this->fileObject->fwrite($output); 
if ($bytes === null) { 
throw new RuntimeException('Unable to write to output file!'); 
} 
} 
} 
24 (c) Carsten Hetzel, 2014
LessUglyCoupling 
 Naja, zumindest kann man jetzt den Dateinamen ändern, 
aber es wird immer noch in eine Datei ein Text geschrieben. 
 Außerdem liefert die Funktion "fwrite()" im Fehlerfall einen 
anderen Rückgabewert als die Methode 
SplFileObject::fwrite() (nämlich "false" statt "null")! 
 Darüber hinaus hat sich aber auch das Verhalten unseres 
Konstruktors geändert: Er wirft plötzlich keine Exception 
mehr! Für den Fall, dass man UnitTests für diese Klasse 
geschrieben hat, müssen wir diese spätestens jetzt anpassen. 
25 (c) Carsten Hetzel, 2014
Eine Sauberere Lösung 
mit Konsequenzen! 
Was halten Sie von folgender Lösung? 
(c) Carsten Hetzel, 2014
Listen Sie Vor- und Nachteile auf! 
class BetterCoupling 
{ 
private $updateOrderHandler; 
public function __construct(UpdateOrderHandler $handler) 
{ 
$this->updateOrderHandler = $handler; 
} 
public function updateOrder($orderId, $newValue) 
{ 
// ... update the order 
$this->updateOrderHandler->onOrderUpdate($orderId, $newValue); 
} 
} 
27 (c) Carsten Hetzel, 2014
Vorteile 
 Die Klasse "BetterCoupling" muss nicht mehr entscheiden, 
was beim Aufruf von "updateOrder" alles passiert. Diese 
Entscheidung trifft nun die Klasse "UpdateOrderHandler". 
 Wir müssen uns nicht mehr um die Behandlung von Fehlern 
kümmern, die uns eigentlich gar nicht interessieren. 
 Es ist kein fester Text mehr vorhanden. 
 ... 
28 (c) Carsten Hetzel, 2014
Nachteile 
 Wir brauchen eine zusätzliche Klasse "UpdateOrderHandler" 
und haben damit eine neue Abhängigkeit eingeführt. 
 Sollte man sich (idealer Weise) entschieden haben, dass 
"UpdateOrderHandler" ein Interface ist, dann haben wir dem 
System sogar noch weitere PHP-Dateien hinzugefügt. 
 Es muss auf jeden Fall ein Handler vorhanden sein, oder wir 
müssen die Implementierung wieder anpassen, so dass die 
Methode "onOrderUpdate()" nicht aufgerufen wird, wenn es 
keinen Handler gibt. 
29 (c) Carsten Hetzel, 2014
Die beste Lösung!? 
Ist es möglich, dass wir ohne Abhängigkeiten auskommen? 
(c) Carsten Hetzel, 2014
Die beste Lösung? 
class NoCoupling 
{ 
public function updateOrder($orderId, $newValue) 
{ 
// ... update the order 
} 
} 
31 (c) Carsten Hetzel, 2014
Und was ist mit unserer 
Ausgabedatei? 
(c) Carsten Hetzel, 2014
Und was ist mit unserer Ausgabedatei? 
class CouplingByInheritance extends NoCoupling 
{ 
public function updateOrder($orderId, $newValue) 
{ 
parent::updateOrder($orderId, $newValue); 
// ... now do whatever you need to do! 
} 
} 
33 (c) Carsten Hetzel, 2014
CouplingByInheritance 
 Diese Klasse kann von uns frei gestaltet werden, ohne dass 
wir die zugrunde liegende Implementierung des fachlichen 
Problems ändern müssen. 
 Die Klasse "NoCoupling" kümmert sich also nur noch um das 
fachliche Problem und die abgeleitete Klasse kann irgend 
eine der bisher gezeigten Lösungsvarianten umsetzen. 
34 (c) Carsten Hetzel, 2014
Wie werden die ganzen 
Ressourcen 
zusammengesetzt? 
(c) Carsten Hetzel, 2014
Wie werden die ganzen Ressourcen 
zusammengesetzt? 
 Das ist ja alles ganz schön, aber jetzt haben wir ein anders 
Problem: 
 Ich habe nichts gewonnen, wenn jetzt an der Stelle, an der 
ich früher "UglyCoupling" eingesetzt habe, eine ganze Reihe 
von anderen Klassen zusätzlich erzeugen muss! 
 Wo früher ... 
36 (c) Carsten Hetzel, 2014
Vorher 
class MyUglyApplication 
{ 
public function doSomething() 
{ 
// ... 
$oderId = $this->providerOrderId(); 
$newValue = $this->providerNewValue(); 
$myUglyClass = new UglyCoupling(); 
$myUglyClass->updateOrder($oderId, $newValue); 
} 
} 
37 (c) Carsten Hetzel, 2014
Jetzt 
class EvenMoreUglyApplication 
{ 
public function doSomething() 
{ 
// ... 
$filename = 'output.txt'; 
$fileObject = new SplFileObject($filename); 
$updateOrderHandler = new UpdateOrderHandler($fileObject); 
$oderId = $this->providerOrderId(); 
$newValue = $this->providerNewValue(); 
$myUglyClass = new CouplingByInheritance($updateOrderHandler); 
$myUglyClass->updateOrder($oderId, $newValue); 
} 
} 
38 (c) Carsten Hetzel, 2014
Refactoring des Ergebnisses 
 Im Client-Code (also unserer Anwendung) ist es scheinbar 
nicht besser sondern schlimmer geworden. 
 Der Code sieht darüber hinaus unleserlich aus. 
 Aber durch einfaches Refactoring lassen sich sehr schöne und 
saubere Methoden erstellen, die die einzelnen Ressourcen 
erstellen 
39 (c) Carsten Hetzel, 2014
Refactoring des Ergebnisses 
class MyInjectingApplication 
{ 
public function doSomething() 
{ 
// ... 
$oderId = $this->providerOrderId(); 
$newValue = $this->providerNewValue(); 
$coupledClass = $this->getCoupledClass(); 
$coupledClass->updateOrder($oderId, $newValue); 
} 
... 
40 (c) Carsten Hetzel, 2014
Refactoring des Ergebnisses 
... 
protected function getCoupledClass() 
{ 
$updateOrderHandler = $this->getUpdateOrderHandler(); 
$coupledClass = new CouplingByInheritance($updateOrderHandler); 
return $coupledClass; 
} 
protected function getUpdateOrderHandler() 
{ 
$fileObject = $this->getFileObject(); 
$updateOrderHandler = new UpdateOrderHandler($fileObject); 
return $updateOrderHandler; 
} 
... 
41 (c) Carsten Hetzel, 2014
Refactoring des Ergebnisses 
... 
protected function getFileObject() 
{ 
return new SplFileObject($this->getFilename()); 
} 
/** 
* @return string 
*/ 
protected function getFilename() 
{ 
return 'output.txt'; 
} 
... 
42 (c) Carsten Hetzel, 2014
Refactoring des Ergebnisses 
 Auf diese Weise bleibt der Code leserlich, die Erstellung jeder 
einzelnen Ressourcen ist in jeweils einer Methode abgebildet 
und aus welchen Sub-Ressourcen eine angeforderte 
Ressource zusammengesetzt ist, kann jederzeit ganz gezielt 
geändert werden. 
 Der letzte verbleibende Schritt an dieser Stelle wäre zu 
entscheiden, welche der Ressourcen immer wieder aufs Neue 
oder nur einmal erstellt werden sollen. 
43 (c) Carsten Hetzel, 2014
Dependency Injection und 
Anwendungen 
(c) Carsten Hetzel, 2014
Dependency Injection und Anwendungen 
 Während das gezeigte Beispiel den Ansatz verfolgt, dass die 
Anwendung selbst der Dependency Injection Container (also 
die Komponente, welche das System "zusammensetzt“) ist, 
gibt es natürlich eine Reihe von Frameworks, welche diese 
Aufgabe durch Konfigurationsdateien erledigen. 
 Interessant dabei ist, dass der jeweilige Dependency Injection 
Container (DIC) in der Regel die Konfigurationsdatei 
"auscompiliert", also in eine ausführbare Datei umwandelt, 
welche tatsächlich sehr ähnlich der oben vorgestellten 
Lösung ist. 
45 (c) Carsten Hetzel, 2014
Dependency Injection und Anwendungen 
 In jedem Fall ermöglicht einem der oben vorgestellte Ansatz 
zu einem späteren Zeitpunkt die Anwendung relativ einfach 
auf einen DIC eines Frameworks umzustellen. 
 Die beteiligten Klassen fordern ja bereits ihre benötigten 
Ressourcen an, statt sie selber zu erstellen. 
46 (c) Carsten Hetzel, 2014
Aufgabe: Anwendungen 
"Komponieren" 
(c) Carsten Hetzel, 2014
Aufgabe: Anwendungen "Komponieren" 
 Eine Anwendung zu Verwaltung von Rechnungen soll als einen 
Anwendungsfall folgendes Verhalten umsetzen: 
 Wenn der Anwender den Prozess "Rechnung Erstellen" 
(generateBill) aufruft wird 
 ein PDF der Rechnung erstellt und 
 ein Rechnungsbericht mit der Anzahl der Seiten der Rechnung ins 
Logfile geschrieben 
 Hinweise: 
 Erstelle "Kommandos" (Commands, siehe Command-Pattern), die 
Aufgaben kapseln 
 Lasse Commands die benötigten Ressourcen anfordern 
 Abstrahiere Teilinformationen (z.B. die Anzahl der Seiten des PDFs) 
48 (c) Carsten Hetzel, 2014
Präsentieren Sie Ihre 
Lösungen 
(c) Carsten Hetzel, 2014
50 (c) Carsten Hetzel, 2014

More Related Content

Viewers also liked

Lebensmittellandschaft
LebensmittellandschaftLebensmittellandschaft
Lebensmittellandschaft
LOSuchaGerm
 
Europa einheit und vielfalt
Europa einheit und vielfaltEuropa einheit und vielfalt
Europa einheit und vielfalt
Landar97
 
Kirchplatz St. Nicolai Lemgo
Kirchplatz St. Nicolai LemgoKirchplatz St. Nicolai Lemgo
Kirchplatz St. Nicolai Lemgo
andreaslangelemgo
 
Medienübergreifende Repositorien - mehr als nur Dokumentenserver
Medienübergreifende Repositorien - mehr als nur DokumentenserverMedienübergreifende Repositorien - mehr als nur Dokumentenserver
Medienübergreifende Repositorien - mehr als nur Dokumentenserver
Ralf Claußnitzer
 

Viewers also liked (17)

Session von Gerald Labitzke zum Thema "Opentransfer" - Non-Profit Camp
Session von Gerald Labitzke zum Thema "Opentransfer" - Non-Profit CampSession von Gerald Labitzke zum Thema "Opentransfer" - Non-Profit Camp
Session von Gerald Labitzke zum Thema "Opentransfer" - Non-Profit Camp
 
Lebensmittellandschaft
LebensmittellandschaftLebensmittellandschaft
Lebensmittellandschaft
 
Die geschichte von der weißes kleid , blaues kleid ' drama, das einen planet...
Die geschichte von der  weißes kleid , blaues kleid ' drama, das einen planet...Die geschichte von der  weißes kleid , blaues kleid ' drama, das einen planet...
Die geschichte von der weißes kleid , blaues kleid ' drama, das einen planet...
 
ERP-Systeme, Stefan Stöckler
ERP-Systeme, Stefan StöcklerERP-Systeme, Stefan Stöckler
ERP-Systeme, Stefan Stöckler
 
Karcher k 5 premium home
Karcher k 5 premium homeKarcher k 5 premium home
Karcher k 5 premium home
 
Location Intelligence - An der Schnittstelle zwischen Geo-Analytics und Busin...
Location Intelligence - An der Schnittstelle zwischen Geo-Analytics und Busin...Location Intelligence - An der Schnittstelle zwischen Geo-Analytics und Busin...
Location Intelligence - An der Schnittstelle zwischen Geo-Analytics und Busin...
 
Europa einheit und vielfalt
Europa einheit und vielfaltEuropa einheit und vielfalt
Europa einheit und vielfalt
 
Kirchplatz St. Nicolai Lemgo
Kirchplatz St. Nicolai LemgoKirchplatz St. Nicolai Lemgo
Kirchplatz St. Nicolai Lemgo
 
China Compulsory Certificate (CCC-Zertifikat)
China Compulsory Certificate (CCC-Zertifikat)China Compulsory Certificate (CCC-Zertifikat)
China Compulsory Certificate (CCC-Zertifikat)
 
DEIN JW - Ausgabe 1 - Dezember 2013
DEIN JW - Ausgabe 1 - Dezember 2013DEIN JW - Ausgabe 1 - Dezember 2013
DEIN JW - Ausgabe 1 - Dezember 2013
 
Kevin
KevinKevin
Kevin
 
Medienübergreifende Repositorien - mehr als nur Dokumentenserver
Medienübergreifende Repositorien - mehr als nur DokumentenserverMedienübergreifende Repositorien - mehr als nur Dokumentenserver
Medienübergreifende Repositorien - mehr als nur Dokumentenserver
 
Karcher ad 3.000 eu ii
Karcher ad 3.000 eu iiKarcher ad 3.000 eu ii
Karcher ad 3.000 eu ii
 
rethink finance - Nachhaltige Investments
rethink finance - Nachhaltige Investmentsrethink finance - Nachhaltige Investments
rethink finance - Nachhaltige Investments
 
Karcher sc 2.600 cb eu
Karcher sc 2.600 cb euKarcher sc 2.600 cb eu
Karcher sc 2.600 cb eu
 
Eine Kurzgeschichte zur Sherpany iOS und Android App
Eine Kurzgeschichte zur Sherpany iOS und Android AppEine Kurzgeschichte zur Sherpany iOS und Android App
Eine Kurzgeschichte zur Sherpany iOS und Android App
 
Karcher 1
Karcher 1Karcher 1
Karcher 1
 

Similar to Dependency Injection - A practical introduction

Einführung Software Testing und Qualitätssicherung
Einführung Software Testing und QualitätssicherungEinführung Software Testing und Qualitätssicherung
Einführung Software Testing und Qualitätssicherung
Christian Baranowski
 
Creasoft - Windows powershell
Creasoft - Windows powershellCreasoft - Windows powershell
Creasoft - Windows powershell
Creasoft AG
 

Similar to Dependency Injection - A practical introduction (13)

2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen
2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen
2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen
 
WiSe 2014 | Softwaretechnologie I _ Funktionen, Zeiger, Strukturen, Bibliotheken
WiSe 2014 | Softwaretechnologie I _ Funktionen, Zeiger, Strukturen, BibliothekenWiSe 2014 | Softwaretechnologie I _ Funktionen, Zeiger, Strukturen, Bibliotheken
WiSe 2014 | Softwaretechnologie I _ Funktionen, Zeiger, Strukturen, Bibliotheken
 
WordPress Plugins und Themes übersetzbar machen - WP Camp 2012 Berlin
WordPress Plugins und Themes übersetzbar machen - WP Camp 2012 BerlinWordPress Plugins und Themes übersetzbar machen - WP Camp 2012 Berlin
WordPress Plugins und Themes übersetzbar machen - WP Camp 2012 Berlin
 
AdvancedTdd
AdvancedTddAdvancedTdd
AdvancedTdd
 
C++ Dependency Management 2.0
C++ Dependency Management 2.0C++ Dependency Management 2.0
C++ Dependency Management 2.0
 
Einführung Software Testing und Qualitätssicherung
Einführung Software Testing und QualitätssicherungEinführung Software Testing und Qualitätssicherung
Einführung Software Testing und Qualitätssicherung
 
TypeScript
TypeScriptTypeScript
TypeScript
 
Creasoft - Windows powershell
Creasoft - Windows powershellCreasoft - Windows powershell
Creasoft - Windows powershell
 
Praesentation TYPO3Camp Berlin Speed mit Extbase
Praesentation TYPO3Camp Berlin Speed mit ExtbasePraesentation TYPO3Camp Berlin Speed mit Extbase
Praesentation TYPO3Camp Berlin Speed mit Extbase
 
Symfony2
Symfony2Symfony2
Symfony2
 
Testgetriebene Softwareentwicklung
Testgetriebene SoftwareentwicklungTestgetriebene Softwareentwicklung
Testgetriebene Softwareentwicklung
 
Web Entwicklung mit PHP - Teil 1
Web Entwicklung mit PHP - Teil 1Web Entwicklung mit PHP - Teil 1
Web Entwicklung mit PHP - Teil 1
 
Einsteiger Workshop
Einsteiger WorkshopEinsteiger Workshop
Einsteiger Workshop
 

Dependency Injection - A practical introduction

  • 1. Dependency Injection Eine Einführung mit grundlegenden Beispielen (c) Carsten Hetzel, 2014
  • 2. Was ist eigentlich das Problem? (c) Carsten Hetzel, 2014
  • 3. Was ist eigentlich das Problem?  Wie üblich bei gängigen Prinzipien aus der Softwareentwicklung liegt dem Prinzip ein wiederkehrendes Problem zugrunde.  Schauen wir uns dazu folgende Klasse an: 3 (c) Carsten Hetzel, 2014
  • 4. class UglyCoupling { private $handle; public function __construct() { $filename = 'output.txt'; $handle = @fopen($filename, 'w'); if (!$handle) { throw new RuntimeException('Unable to open output file!'); } $this->handle = $handle; } public function updateOrder($orderId, $newValue) { // ... update the order $output = 'The value of order ' . $orderId . ' has been changed to ' . $newValue . '!'; $bytes = fwrite($this->handle, $output); if ($bytes === false) { throw new RuntimeException('Unable to write to output file!'); } } } 4 (c) Carsten Hetzel, 2014
  • 5. Welche Probleme hat diese Klasse? (c) Carsten Hetzel, 2014
  • 6. Welche Probleme hat diese Klasse?  Der Dateiname ist nicht änderbar  Es ist nicht klar, wo im Dateisystem die Datei ggf. erzeugt wird  Die Klasse UglyCoupling kann nicht genutzt werden, wenn die Datei nicht geschrieben werden kann  Die Klasse wirft Exceptions bei Fehlern, die mit ihrer eigentlichen Funktion nichts zu tun haben  Wenn weitere Dinge beim Aufruf von "updateOrder()" passieren sollen, müssen sie in "UglyCoupling" hinzugefügt werden  Tests erzeugen Dateien im Dateisystem, auch wenn das auf dem Testsystem vielleicht gar nicht gewünscht ist 6 (c) Carsten Hetzel, 2014
  • 7. Was ist eigentlich das Problem?  Die Datei wird nicht geschlossen (bzw. erst, wenn der PHP-Prozess endet)  Jede Instanz der Klasse UglyCoupling öffnet die selbe Datei  Wenn der Inhalt des "outputs" oder das Ausgabeformat von Text auf PDF geändert werden soll, muss die Klasse UglyCoupling angepasst werden  ... 7 (c) Carsten Hetzel, 2014
  • 8. Was ist Dependency Injection? (c) Carsten Hetzel, 2014
  • 9. Was ist Dependency Injection?  Dependency Injection fordert uns auf Ressourcen anzufordern, statt sie selber bereit zu stellen. Es gibt drei Wege Ressourcen anzufordern:  1. Constructor Injection  2. Setter Injection  3. Interface Injection 9 (c) Carsten Hetzel, 2014
  • 10. Constructor Injection  Klassen, die Ressourcen für ihre Arbeit benötigen, fordern diese Ressourcen über ihren Konstruktor an.  Damit ist gewährleistet, dass die Klasse von Beginn an über die Ressourcen verfügt, die sie benötigt. 10 (c) Carsten Hetzel, 2014
  • 11. Constructor Injection class ConstructorInjection { /** * @var Service */ private $service; public function __construct(Service $service) { $this->service = $service; } } 11 (c) Carsten Hetzel, 2014
  • 12. Constructor Injection  Ein klarer Nachteil der Constructor Injection ist, dass sehr früh im Application Lifecycle Ressourcen bereitgestellt werden, die evtl. gar nicht zum Einsatz kommen.  Eine Konsequenz aus Constructor Injection ist, Ressourcen so kostengünstig wie möglich zu erstellen, damit keine überflüssige Arbeiten vorgenommen werden. 12 (c) Carsten Hetzel, 2014
  • 13. Setter Injection  Benötigte Ressourcen werden über Set-Methoden bereitgestellt. Diese Methoden können ggf. deutlich nach der Erstellung des Anfordernden Objekts aufgerufen und dem Klienten bereitgestellt werden.  Dabei besteht aber auch das Risiko einen ungültigen Zustand, ein unvorhersehbares Verhalten oder sogar einen Fehler hervorzurufen.  Ggf. muss also die Verfügbarkeit der Ressourcen geprüft werden! 13 (c) Carsten Hetzel, 2014
  • 14. Setter Injection class SetterInjection { /** * @var Service */ private $service; /** * @param Service $service */ public function setService(Service $service) { $this->service = $service; } } 14 (c) Carsten Hetzel, 2014
  • 15. Interface Injection  Benötigt eine Klasse eine bestimmte Ressource, dann implementiert sie eine Interface, welches das Injizieren dieser Ressource anfordert.  D.h. das Interface fordert die Implementierung z.B. einer "inject"-Methode, die als Parameter die entsprechende Ressource erwartet. 15 (c) Carsten Hetzel, 2014
  • 16. Interface Injection class InterfaceInjection implements InjectSercvice { /** * @var Service */ private $service; public function injectService(Service $service) { $this->service = $service; } } 16 (c) Carsten Hetzel, 2014
  • 17. Was ist eigentlich mit einer "Ressource" gemeint? (c) Carsten Hetzel, 2014
  • 18. Was sind „Ressourcen“?  Eine Ressource kann eigentlich alles mögliche sein - von einer einfachen Zahl bis zu einer komplexen Service-Klasse.  Es ist also keines Falls so, dass nur Services über Dependency Injection angefordert werden sollen.  Ein klassisches Beispiel sind Parameter für eine Datenbankverbindung. 18 (c) Carsten Hetzel, 2014
  • 19. Aufgabe: Überarbeiten sie "UglyCoupling“ (c) Carsten Hetzel, 2014
  • 20. Aufgabe: „UglyCoupling“  Überarbeiten Sie die Klasse "UglyCoupling" so, dass die benötigten Ressourcen angefordert werden.  Bitte führen Sie diese Aufgabe in Teams durch und diskutieren Sie Ihre Ansätze.  Sie haben 5 Minuten Zeit! 20 (c) Carsten Hetzel, 2014
  • 21. class UglyCoupling { private $handle; public function __construct() { $filename = 'output.txt'; $handle = @fopen($filename, 'w'); if (!$handle) { throw new RuntimeException('Unable to open output file!'); } $this->handle = $handle; } public function updateOrder($orderId, $newValue) { // ... update the order $output = 'Order ' . $orderId . ' has been changed to ' . $newValue . '!'; $bytes = fwrite($this->handle, $output); if ($bytes === false) { throw new RuntimeException('Unable to write to output file!'); } } } 21 (c) Carsten Hetzel, 2014
  • 22. Lösungen? (c) Carsten Hetzel, 2014
  • 23. Ist das besser? Bitte diskutieren Sie folgende Lösung! (c) Carsten Hetzel, 2014
  • 24. class LessUglyCoupling { private $fileObject; public function __construct(SplFileObject $fileObject) { $this->fileObject = $fileObject; } public function updateOrder($orderId, $newValue) { // ... update the order $output = 'Order ' . $orderId . ' has been changed to ' . $newValue . '!'; $bytes = $this->fileObject->fwrite($output); if ($bytes === null) { throw new RuntimeException('Unable to write to output file!'); } } } 24 (c) Carsten Hetzel, 2014
  • 25. LessUglyCoupling  Naja, zumindest kann man jetzt den Dateinamen ändern, aber es wird immer noch in eine Datei ein Text geschrieben.  Außerdem liefert die Funktion "fwrite()" im Fehlerfall einen anderen Rückgabewert als die Methode SplFileObject::fwrite() (nämlich "false" statt "null")!  Darüber hinaus hat sich aber auch das Verhalten unseres Konstruktors geändert: Er wirft plötzlich keine Exception mehr! Für den Fall, dass man UnitTests für diese Klasse geschrieben hat, müssen wir diese spätestens jetzt anpassen. 25 (c) Carsten Hetzel, 2014
  • 26. Eine Sauberere Lösung mit Konsequenzen! Was halten Sie von folgender Lösung? (c) Carsten Hetzel, 2014
  • 27. Listen Sie Vor- und Nachteile auf! class BetterCoupling { private $updateOrderHandler; public function __construct(UpdateOrderHandler $handler) { $this->updateOrderHandler = $handler; } public function updateOrder($orderId, $newValue) { // ... update the order $this->updateOrderHandler->onOrderUpdate($orderId, $newValue); } } 27 (c) Carsten Hetzel, 2014
  • 28. Vorteile  Die Klasse "BetterCoupling" muss nicht mehr entscheiden, was beim Aufruf von "updateOrder" alles passiert. Diese Entscheidung trifft nun die Klasse "UpdateOrderHandler".  Wir müssen uns nicht mehr um die Behandlung von Fehlern kümmern, die uns eigentlich gar nicht interessieren.  Es ist kein fester Text mehr vorhanden.  ... 28 (c) Carsten Hetzel, 2014
  • 29. Nachteile  Wir brauchen eine zusätzliche Klasse "UpdateOrderHandler" und haben damit eine neue Abhängigkeit eingeführt.  Sollte man sich (idealer Weise) entschieden haben, dass "UpdateOrderHandler" ein Interface ist, dann haben wir dem System sogar noch weitere PHP-Dateien hinzugefügt.  Es muss auf jeden Fall ein Handler vorhanden sein, oder wir müssen die Implementierung wieder anpassen, so dass die Methode "onOrderUpdate()" nicht aufgerufen wird, wenn es keinen Handler gibt. 29 (c) Carsten Hetzel, 2014
  • 30. Die beste Lösung!? Ist es möglich, dass wir ohne Abhängigkeiten auskommen? (c) Carsten Hetzel, 2014
  • 31. Die beste Lösung? class NoCoupling { public function updateOrder($orderId, $newValue) { // ... update the order } } 31 (c) Carsten Hetzel, 2014
  • 32. Und was ist mit unserer Ausgabedatei? (c) Carsten Hetzel, 2014
  • 33. Und was ist mit unserer Ausgabedatei? class CouplingByInheritance extends NoCoupling { public function updateOrder($orderId, $newValue) { parent::updateOrder($orderId, $newValue); // ... now do whatever you need to do! } } 33 (c) Carsten Hetzel, 2014
  • 34. CouplingByInheritance  Diese Klasse kann von uns frei gestaltet werden, ohne dass wir die zugrunde liegende Implementierung des fachlichen Problems ändern müssen.  Die Klasse "NoCoupling" kümmert sich also nur noch um das fachliche Problem und die abgeleitete Klasse kann irgend eine der bisher gezeigten Lösungsvarianten umsetzen. 34 (c) Carsten Hetzel, 2014
  • 35. Wie werden die ganzen Ressourcen zusammengesetzt? (c) Carsten Hetzel, 2014
  • 36. Wie werden die ganzen Ressourcen zusammengesetzt?  Das ist ja alles ganz schön, aber jetzt haben wir ein anders Problem:  Ich habe nichts gewonnen, wenn jetzt an der Stelle, an der ich früher "UglyCoupling" eingesetzt habe, eine ganze Reihe von anderen Klassen zusätzlich erzeugen muss!  Wo früher ... 36 (c) Carsten Hetzel, 2014
  • 37. Vorher class MyUglyApplication { public function doSomething() { // ... $oderId = $this->providerOrderId(); $newValue = $this->providerNewValue(); $myUglyClass = new UglyCoupling(); $myUglyClass->updateOrder($oderId, $newValue); } } 37 (c) Carsten Hetzel, 2014
  • 38. Jetzt class EvenMoreUglyApplication { public function doSomething() { // ... $filename = 'output.txt'; $fileObject = new SplFileObject($filename); $updateOrderHandler = new UpdateOrderHandler($fileObject); $oderId = $this->providerOrderId(); $newValue = $this->providerNewValue(); $myUglyClass = new CouplingByInheritance($updateOrderHandler); $myUglyClass->updateOrder($oderId, $newValue); } } 38 (c) Carsten Hetzel, 2014
  • 39. Refactoring des Ergebnisses  Im Client-Code (also unserer Anwendung) ist es scheinbar nicht besser sondern schlimmer geworden.  Der Code sieht darüber hinaus unleserlich aus.  Aber durch einfaches Refactoring lassen sich sehr schöne und saubere Methoden erstellen, die die einzelnen Ressourcen erstellen 39 (c) Carsten Hetzel, 2014
  • 40. Refactoring des Ergebnisses class MyInjectingApplication { public function doSomething() { // ... $oderId = $this->providerOrderId(); $newValue = $this->providerNewValue(); $coupledClass = $this->getCoupledClass(); $coupledClass->updateOrder($oderId, $newValue); } ... 40 (c) Carsten Hetzel, 2014
  • 41. Refactoring des Ergebnisses ... protected function getCoupledClass() { $updateOrderHandler = $this->getUpdateOrderHandler(); $coupledClass = new CouplingByInheritance($updateOrderHandler); return $coupledClass; } protected function getUpdateOrderHandler() { $fileObject = $this->getFileObject(); $updateOrderHandler = new UpdateOrderHandler($fileObject); return $updateOrderHandler; } ... 41 (c) Carsten Hetzel, 2014
  • 42. Refactoring des Ergebnisses ... protected function getFileObject() { return new SplFileObject($this->getFilename()); } /** * @return string */ protected function getFilename() { return 'output.txt'; } ... 42 (c) Carsten Hetzel, 2014
  • 43. Refactoring des Ergebnisses  Auf diese Weise bleibt der Code leserlich, die Erstellung jeder einzelnen Ressourcen ist in jeweils einer Methode abgebildet und aus welchen Sub-Ressourcen eine angeforderte Ressource zusammengesetzt ist, kann jederzeit ganz gezielt geändert werden.  Der letzte verbleibende Schritt an dieser Stelle wäre zu entscheiden, welche der Ressourcen immer wieder aufs Neue oder nur einmal erstellt werden sollen. 43 (c) Carsten Hetzel, 2014
  • 44. Dependency Injection und Anwendungen (c) Carsten Hetzel, 2014
  • 45. Dependency Injection und Anwendungen  Während das gezeigte Beispiel den Ansatz verfolgt, dass die Anwendung selbst der Dependency Injection Container (also die Komponente, welche das System "zusammensetzt“) ist, gibt es natürlich eine Reihe von Frameworks, welche diese Aufgabe durch Konfigurationsdateien erledigen.  Interessant dabei ist, dass der jeweilige Dependency Injection Container (DIC) in der Regel die Konfigurationsdatei "auscompiliert", also in eine ausführbare Datei umwandelt, welche tatsächlich sehr ähnlich der oben vorgestellten Lösung ist. 45 (c) Carsten Hetzel, 2014
  • 46. Dependency Injection und Anwendungen  In jedem Fall ermöglicht einem der oben vorgestellte Ansatz zu einem späteren Zeitpunkt die Anwendung relativ einfach auf einen DIC eines Frameworks umzustellen.  Die beteiligten Klassen fordern ja bereits ihre benötigten Ressourcen an, statt sie selber zu erstellen. 46 (c) Carsten Hetzel, 2014
  • 47. Aufgabe: Anwendungen "Komponieren" (c) Carsten Hetzel, 2014
  • 48. Aufgabe: Anwendungen "Komponieren"  Eine Anwendung zu Verwaltung von Rechnungen soll als einen Anwendungsfall folgendes Verhalten umsetzen:  Wenn der Anwender den Prozess "Rechnung Erstellen" (generateBill) aufruft wird  ein PDF der Rechnung erstellt und  ein Rechnungsbericht mit der Anzahl der Seiten der Rechnung ins Logfile geschrieben  Hinweise:  Erstelle "Kommandos" (Commands, siehe Command-Pattern), die Aufgaben kapseln  Lasse Commands die benötigten Ressourcen anfordern  Abstrahiere Teilinformationen (z.B. die Anzahl der Seiten des PDFs) 48 (c) Carsten Hetzel, 2014
  • 49. Präsentieren Sie Ihre Lösungen (c) Carsten Hetzel, 2014
  • 50. 50 (c) Carsten Hetzel, 2014