Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dependency Injection

Dependency Injection

Meine Slides von der PHPUG und DevCon Hamburg, 2011

Mario Mueller

April 27, 2012
Tweet

More Decks by Mario Mueller

Other Decks in Technology

Transcript

  1. Inversion of Control - IoC Paradigma aus dem Software Design

    / Architektur Widerspricht dem "klassischen" Ansatz "jeder besorgt sich seine Sachen selbst" Das Objekt, bzw. die Klasse gibt die Steuerung der Abhängigkeiten aus der Hand Sog. Hollywood-Prinzip: "Don't call us, we call you"
  2. Dependency Injection - DI Ist ein Entwurfsmuster Betrachtet Abhängigkeiten zwischen

    Objekten Verwendet ein erweitertes Factory Entwurfsmuster als Basis Ist eine Anwendung des IoC Paradigmas Die Anwendung des Musters auf bestehenden Code ist ein Refactoring Prozess Es gibt Bibliotheken in PHP, die am Ende des Prozesses stehen
  3. So sieht es häufig aus... <?php class Foo { private

    $oDbConn; public function __construct() { $this->oDbConn = new MyMysqlDriver(CPATH); } }
  4. Besser, aber immer noch gruselig... <?php class Foo { private

    $oDbConn; private $sDriverClass = MyConfig::getDbClass(); public function __construct() { $this->oDbConn = new $this->sDriverClass(); } }
  5. Was läuft da schief? Die Klasse kümmert sich um das

    Erzeugen von kritischen Objekten Der Klassenname der Abhängigkeit ist fest in der instanzierenden Klasse "verdrahtet" Selbst beim Auslagern des Klassennamens der Abhängigkeit kümmert sich dennoch die Klasse selbst um die Bereitstellung der Instanz
  6. Ok, seh ich ein... aber... DI ist nun so viel

    besser? Ging bis jetzt auch ohne ...
  7. Ja?

  8. Zuerst: Was heißt Abhängigkeit? Eine Abhängigkeit äußert sich durch die

    Referenzierung oder Verwendung einer konkreten Implementierung in einer Klasse. class FooImpl { public function __construct() { $oVar = new BarImpl(); } } class FooImpl { public function __construct(BarImpl $oParam) { // ... } }
  9. Welche Arten von "Injection" gibt es? Constructor Injection Instanzen werden

    über den Konstruktor beim Erstellen der Instanz übergeben und in der Instanz vorgehalten und / oder weiter durchgereicht. Getter / Setter Injection Instanzen werden per setFoo(FooInterface $oFoo) und getFoo() gesetzt und geholt. Man greift nicht mehr auf die Eigenschaft direkt zu, da auch die Beschaffung der Instanz verborgen wird.
  10. Schritt 1: Don't do constructor work Konstruktoren sollen nur den

    ersten Zustand einer Instanz beschreiben. Schlecht: public function __construct() { $this->oDb = new DbConnImpl( "localhost", "user", "pass"); } Besser: public function __construct(DbConnImpl $oDb) { $this->oDb = $oDb; }
  11. Schritt 2: Design by contract Anstelle der Implementierung sollen Interfaces

    stehen. Dieser Schritt löst die Abhängigkeit zu einer konkreten Implementierung. statt: public function setDbConn(MySQLDbConn $oConn) {} lieber: public function setDbConn(Queryable $oConn) {} => Vorteil: Theoretisch kann die DB nun auch eine SQLite sein, solang das Interface "Queryable" erfüllt ist.
  12. Schritt 3: Weg mit den Globals Damit sind sowohl globale

    Variablen, als auch Singletons in Konstruktoren und Methoden gemeint! denn: public function __construct() { $this->oVar = DbConn::getInstance(); } ist das Gleiche wie: public function __construct() { $this->oVar = $GLOBALS['dbconn']; } Es gibt jedoch Ausnahmen, bei denen es Sinn macht einen Singleton einzusetzen!
  13. Aufgaben des Bootstrappings Erzeugung der global benötigten Objekte (z. B.

    DbConn) Weiterverteilung der Objekte bei der Initialisierung des Frameworks z. B. MVC-Framework 1. Bootstrap erzeugt DbConn 2. Bootstrap erzeugt MVC-Router 3. Bootstrap übergibt DbConn an MVC Router 4. MVC Router findet passenden Controller 5. MVC Router übergibt DbConn an Controller 6. Controller erzeugt Service Layer 7. Controller übergibt DbConn an Service Layer => DbConn ist nur ein Mal erzeugt worden!
  14. Was machen IoC Container? Sie kennen die Abhängigkeiten einer Klasse

    Sie erzeugen Instanzen der geforderten Klasse Sie "konfigurieren" die neue Instanz mit den bekannten Abhängigkeiten Manche können Abhängigkeitsgraphen auflösen => Klasse A braucht eine Instanz von B => B braucht im Konstruktor eine Instanz von C Meist erkennen diese dann auch "Circular References" => Wenn C im o. g. Beispiel eine Instanz von A benötigen würde
  15. Verschiedene Ansätze: Deklarativ Im Bootstrap einer App wird der Container

    konfiguriert. Hierbei wird die Implementierung für ein Interface angegeben. $oCont = Container::getInstance() $oCont->useDep('Queryable', 'MySqlQueryableImpl'); $oCont->manageClass('Foo', 'aNickName'); $oFoo = $oCont->get('aNickName'); assert (($oFoo instanceof Foo) === true) && (($oFoo->getConn() instanceof Queryable) === true) Ein assert auf MySqlQueryableImpl würde ebenfalls funktionieren, aber gegen das Prinzip verstoßen.
  16. Verschiedene Ansätze: Annotation Dieser Ansatz ist in PHP noch recht

    neu. Eine Klasse wird per DocBlock annotiert. class Foo { /** * @inject */ public function __construct(Queryable $oConn){ } } $oCont = Container::getInstance(); $oFoo = $oCont->get('Foo');
  17. Verschiedene Ansätze: Konfiguration Eine externe Konfigurationsdatei wird verwendet. Hier ein

    Beispiel aus einer FLOW3 YAML Konfiguration: F3\MyPackage\Foo: properties: bar: { object: F3\MyPacka ge\BarInterface } $oFoo = Container::getInstance()->get('Foo')
  18. Ein paar Links Blogartikel über Symfony's DIC http://usrportage.de/archives/926-Dependency-Injection-Container- Refactorings,-Part-One.html Martin

    Fowler über "Injection" http://martinfowler.com/articles/injection.html Flow3 Object Framework http://flow3.typo3.org/documentation/manuals/flow3/flow3. objectframework/#flow3.objectframework.objectdependencies