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 w...
class UglyCoupling 
{ 
private $handle; 
public function __construct() 
{ 
$filename = 'output.txt'; 
$handle = @fopen($fi...
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 gg...
Was ist eigentlich das Problem? 
 Die Datei wird nicht geschlossen (bzw. erst, wenn der PHP-Prozess 
endet) 
 Jede Insta...
Was ist Dependency 
Injection? 
(c) Carsten Hetzel, 2014
Was ist Dependency Injection? 
 Dependency Injection fordert uns auf Ressourcen 
anzufordern, statt sie selber bereit zu ...
Constructor Injection 
 Klassen, die Ressourcen für ihre Arbeit benötigen, fordern 
diese Ressourcen über ihren Konstrukt...
Constructor Injection 
class ConstructorInjection 
{ 
/** 
* @var Service 
*/ 
private $service; 
public function __constr...
Constructor Injection 
 Ein klarer Nachteil der Constructor Injection ist, dass sehr 
früh im Application Lifecycle Resso...
Setter Injection 
 Benötigte Ressourcen werden über Set-Methoden 
bereitgestellt. Diese Methoden können ggf. deutlich nac...
Setter Injection 
class SetterInjection 
{ 
/** 
* @var Service 
*/ 
private $service; 
/** 
* @param Service $service 
*/...
Interface Injection 
 Benötigt eine Klasse eine bestimmte Ressource, dann 
implementiert sie eine Interface, welches das ...
Interface Injection 
class InterfaceInjection implements InjectSercvice 
{ 
/** 
* @var Service 
*/ 
private $service; 
pu...
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 komp...
Aufgabe: Überarbeiten sie 
"UglyCoupling“ 
(c) Carsten Hetzel, 2014
Aufgabe: „UglyCoupling“ 
 Überarbeiten Sie die Klasse "UglyCoupling" so, dass die 
benötigten Ressourcen angefordert werd...
class UglyCoupling 
{ 
private $handle; 
public function __construct() { 
$filename = 'output.txt'; 
$handle = @fopen($fil...
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->fileObj...
LessUglyCoupling 
 Naja, zumindest kann man jetzt den Dateinamen ändern, 
aber es wird immer noch in eine Datei ein Text ...
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(Upd...
Vorteile 
 Die Klasse "BetterCoupling" muss nicht mehr entscheiden, 
was beim Aufruf von "updateOrder" alles passiert. Di...
Nachteile 
 Wir brauchen eine zusätzliche Klasse "UpdateOrderHandler" 
und haben damit eine neue Abhängigkeit eingeführt....
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 
} 
}...
Und was ist mit unserer 
Ausgabedatei? 
(c) Carsten Hetzel, 2014
Und was ist mit unserer Ausgabedatei? 
class CouplingByInheritance extends NoCoupling 
{ 
public function updateOrder($ord...
CouplingByInheritance 
 Diese Klasse kann von uns frei gestaltet werden, ohne dass 
wir die zugrunde liegende Implementie...
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 
Proble...
Vorher 
class MyUglyApplication 
{ 
public function doSomething() 
{ 
// ... 
$oderId = $this->providerOrderId(); 
$newVal...
Jetzt 
class EvenMoreUglyApplication 
{ 
public function doSomething() 
{ 
// ... 
$filename = 'output.txt'; 
$fileObject ...
Refactoring des Ergebnisses 
 Im Client-Code (also unserer Anwendung) ist es scheinbar 
nicht besser sondern schlimmer ge...
Refactoring des Ergebnisses 
class MyInjectingApplication 
{ 
public function doSomething() 
{ 
// ... 
$oderId = $this->p...
Refactoring des Ergebnisses 
... 
protected function getCoupledClass() 
{ 
$updateOrderHandler = $this->getUpdateOrderHand...
Refactoring des Ergebnisses 
... 
protected function getFileObject() 
{ 
return new SplFileObject($this->getFilename()); 
...
Refactoring des Ergebnisses 
 Auf diese Weise bleibt der Code leserlich, die Erstellung jeder 
einzelnen Ressourcen ist i...
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 und Anwendungen 
 In jedem Fall ermöglicht einem der oben vorgestellte Ansatz 
zu einem späteren Zei...
Aufgabe: Anwendungen 
"Komponieren" 
(c) Carsten Hetzel, 2014
Aufgabe: Anwendungen "Komponieren" 
 Eine Anwendung zu Verwaltung von Rechnungen soll als einen 
Anwendungsfall folgendes...
Präsentieren Sie Ihre 
Lösungen 
(c) Carsten Hetzel, 2014
50 (c) Carsten Hetzel, 2014
Nächste SlideShare
Wird geladen in …5
×

Dependency Injection - A practical introduction

342 Aufrufe

Veröffentlicht am

Workshop zum Thema Dependency Injection.

Veröffentlicht in: Präsentationen & Vorträge
0 Kommentare
0 Gefällt mir
Statistik
Notizen
  • Als Erste(r) kommentieren

  • Gehören Sie zu den Ersten, denen das gefällt!

Keine Downloads
Aufrufe
Aufrufe insgesamt
342
Auf SlideShare
0
Aus Einbettungen
0
Anzahl an Einbettungen
3
Aktionen
Geteilt
0
Downloads
2
Kommentare
0
Gefällt mir
0
Einbettungen 0
Keine Einbettungen

Keine Notizen für die Folie

Dependency Injection - A practical introduction

  1. 1. Dependency Injection Eine Einführung mit grundlegenden Beispielen (c) Carsten Hetzel, 2014
  2. 2. Was ist eigentlich das Problem? (c) Carsten Hetzel, 2014
  3. 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. 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. 5. Welche Probleme hat diese Klasse? (c) Carsten Hetzel, 2014
  6. 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. 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. 8. Was ist Dependency Injection? (c) Carsten Hetzel, 2014
  9. 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. 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. 11. Constructor Injection class ConstructorInjection { /** * @var Service */ private $service; public function __construct(Service $service) { $this->service = $service; } } 11 (c) Carsten Hetzel, 2014
  12. 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. 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. 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. 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. 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. 17. Was ist eigentlich mit einer "Ressource" gemeint? (c) Carsten Hetzel, 2014
  18. 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. 19. Aufgabe: Überarbeiten sie "UglyCoupling“ (c) Carsten Hetzel, 2014
  20. 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. 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. 22. Lösungen? (c) Carsten Hetzel, 2014
  23. 23. Ist das besser? Bitte diskutieren Sie folgende Lösung! (c) Carsten Hetzel, 2014
  24. 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. 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. 26. Eine Sauberere Lösung mit Konsequenzen! Was halten Sie von folgender Lösung? (c) Carsten Hetzel, 2014
  27. 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. 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. 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. 30. Die beste Lösung!? Ist es möglich, dass wir ohne Abhängigkeiten auskommen? (c) Carsten Hetzel, 2014
  31. 31. Die beste Lösung? class NoCoupling { public function updateOrder($orderId, $newValue) { // ... update the order } } 31 (c) Carsten Hetzel, 2014
  32. 32. Und was ist mit unserer Ausgabedatei? (c) Carsten Hetzel, 2014
  33. 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. 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. 35. Wie werden die ganzen Ressourcen zusammengesetzt? (c) Carsten Hetzel, 2014
  36. 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. 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. 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. 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. 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. 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. 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. 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. 44. Dependency Injection und Anwendungen (c) Carsten Hetzel, 2014
  45. 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. 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. 47. Aufgabe: Anwendungen "Komponieren" (c) Carsten Hetzel, 2014
  48. 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. 49. Präsentieren Sie Ihre Lösungen (c) Carsten Hetzel, 2014
  50. 50. 50 (c) Carsten Hetzel, 2014

×