Date post: | 13-Jan-2015 |
Category: |
Technology |
Upload: | stephan-hochdoerfer |
View: | 1,025 times |
Download: | 3 times |
Dependency InjectionWas, wie, warum?
Stephan Hochdörfer, bitExpert AG
Dependency Injection – Was, wie warum?
Über mich
Stephan Hochdörfer
Head of IT der bitExpert AG, Mannheim
PHP`ler seit 1999
@shochdoerfer
Dependency Injection – Was, wie warum?
DI ist nicht alles. Es geht um mehr.
Dependency Injection – Was, wie warum?
Separation of Concerns
Design by Contract
Dependency Injection – Was, wie warum?
Dependency Injection – Was, wie warum?
Dependency Injection
Dependency Injection – Was, wie warum?
Dependency Injection
"[...] zur Laufzeit die Abhängigkeiten eines Objekts diesem von einem
anderen Objekt als Referenzen zur Verfügung gestellt werden."
(Wikipedia)
Dependency Injection – Was, wie warum?
Was sind Dependencies?
Dependency Injection – Was, wie warum?
Sind Dependencies schlecht?
Dependency Injection – Was, wie warum?
Dependencies: Starke Kopplung
Keine Wiederverwendung!
Dependency Injection – Was, wie warum?
Keine Isolation, nicht testbar!
Dependency Injection – Was, wie warum?
Dependency Injection – Was, wie warum?
Dependency Wahnsinn!
Was ist Dependency Injection?
Dependency Injection – Was, wie warum?
Was ist Dependency Injection?
Dependency Injection – Was, wie warum?
new DeletePage(new PageManager());
Was ist Dependency Injection?
Consumer
Dependency Injection – Was, wie warum?
Was ist Dependency Injection?
Consumer Dependencies
Dependency Injection – Was, wie warum?
Was ist Dependency Injection?
Consumer Dependencies Container
Dependency Injection – Was, wie warum?
Was ist Dependency Injection?
Consumer Dependencies Container
Dependency Injection – Was, wie warum?
s
Dependency Injection – Was, wie warum?
„new“ is evil!
<?phpclass DeletePage extends Mvc_Action_AAction {
private $pageManager;
public function __construct() {$this->pageManager = new PageManager();
}
protected function execute(Mvc_Request $request) {$this->pageManager->delete(
(int) $request->get('pageId'));
}}
Dependency Injection – Was, wie warum?
„new“ is evil!
Dependency Injection – Was, wie warum?
„new“ is evil!
<?phpclass DeletePage extends Mvc_Action_AAction {
private $pageManager;
public function __construct(PageManager $pm) {$this->pageManager = $pm;
}
protected function execute(Mvc_Request $request) {$this->pageManager->delete(
(int) $request->get('pageId'));
}}
"High-level modules should not depend on low-level modules.
Both should depend on abstractions."Robert C. Martin
Dependency Injection – Was, wie warum?
Dependency Injection – Was, wie warum?
„new“ is evil!
<?phpclass DeletePage extends Mvc_Action_AAction {
private $pageManager;
public function __construct(IPageManager $pm) {$this->pageManager = $pm;
}
protected function execute(Mvc_Request $request) {$this->pageManager->delete(
(int) $request->get('pageId'));
}}
Wie verwaltet man Dependencies?
Dependency Injection – Was, wie warum?
Wie verwaltet man Dependencies?
Simple Container vs. Full stacked DI Framework
Dependency Injection – Was, wie warum?
Der Container nimmt uns die Arbeit ab!
Dependency Injection – Was, wie warum?
Wie Dependencies injizieren?
Dependency Injection – Was, wie warum?
Constructor Injection<?php
class MySampleService implements IMySampleService { /** * @var ISampleDao */ private $sampleDao;
public function __construct(ISampleDao $sampleDao) {$this->sampleDao = $sampleDao;
}}
Dependency Injection – Was, wie warum?
Setter Injection<?php
class MySampleService implements IMySampleService { /** * @var ISampleDao */ private $sampleDao;
public function setSampleDao(ISampleDao $sampleDao){$this->sampleDao = $sampleDao;
}}
Dependency Injection – Was, wie warum?
Interface Injection<?php
interface IApplicationContextAware { public function setCtx(IApplicationContext $ctx);}
Dependency Injection – Was, wie warum?
Interface Injection<?php
class MySampleService implements IMySampleService,IApplicationContextAware { /** * @var IApplicationContext */ private $ctx;
public function setCtx(IApplicationContext $ctx) {$this->ctx = $ctx;
}}
Dependency Injection – Was, wie warum?
Property Injection
Dependency Injection – Was, wie warum?
Property Injection
Dependency Injection – Was, wie warum?
"NEIN NEIN NEIN!"David Zülke
Wie funktioniert das „Wiring“?
Dependency Injection – Was, wie warum?
Dependency Injection – Was, wie warum?
Annotations<?php
class MySampleService implements IMySampleService { private $sampleDao;
/** * @Inject */ public function __construct(ISampleDao $sampleDao) {
$this->sampleDao = $sampleDao; }}
Dependency Injection – Was, wie warum?
Annotations<?php
class MySampleService implements IMySampleService { private $sampleDao;
/** * @Inject * @Named('TheSampleDao') */ public function __construct(ISampleDao $sampleDao) {
$this->sampleDao = $sampleDao; }}
Externe Konfiguration - XML
<?xml version="1.0" encoding="UTF-8" ?><beans>
<bean id="SampleDao" class="SampleDao"><constructor-arg value="app_sample" /><constructor-arg value="iSampleId" /><constructor-arg value="BoSample" />
</bean>
<bean id="SampleService" class="MySampleService"><constructor-arg ref="SampleDao" />
</bean></beans>
Dependency Injection – Was, wie warum?
Externe Konfiguration - YAML
services: SampleDao: class: SampleDao arguments: ['app_sample', 'iSampleId', 'BoSample'] SampleService: class: SampleService arguments: [@SampleDao]
Dependency Injection – Was, wie warum?
Externe Konfiguration - PHP<?phpclass BeanCache extends Beanfactory_Container_PHP {
protected function createSampleDao() {$oBean = new SampleDao('app_sample',
'iSampleId', 'BoSample');return $oBean;
}
protected function createMySampleService() {$oBean = new MySampleService(
$this->getBean('SampleDao'));return $oBean;
}}
Dependency Injection – Was, wie warum?
Interne vs. Externe Konfiguration
Dependency Injection – Was, wie warum?
Interne vs. Externe Konfiguration
Dependency Injection – Was, wie warum?
Klassenkonfiguration
vs.
Instanzkonfiguration
Dependency Injection – Was, wie warum?
Was bringt mir das? Beispiel bitte.
Unittesting einfach gemacht
Dependency Injection – Was, wie warum?
Unittesting einfach gemacht<?phprequire_once 'PHPUnit/Framework.php';
class ServiceTest extends PHPUnit_Framework_TestCase { public function testSampleService() {
// set up dependencies $sampleDao = $this->getMock('ISampleDao'); $service = new MySampleService($sampleDao);
// run test case$return = $service->doWork();
// check assertions$this->assertTrue($return);
}}
Dependency Injection – Was, wie warum?
Eine Klasse, mehrfache Ausprägung
Dependency Injection – Was, wie warum?
Eine Klasse, mehrfache Ausprägung
Page ExporterPage Exporter
Released / PublishedPages
Released / PublishedPages
WorkingcopyPages
WorkingcopyPages
Dependency Injection – Was, wie warum?
Eine Klasse, mehrfache Ausprägung<?phpabstract class PageExporter { protected function setPageDao(IPageDao $pageDao) { $this->pageDao = $pageDao; }}
Dependency Injection – Was, wie warum?
Eine Klasse, mehrfache Ausprägung<?phpabstract class PageExporter { protected function setPageDao(IPageDao $pageDao) { $this->pageDao = $pageDao; }}
Zur Erinnerung: Der Vertrag!
Dependency Injection – Was, wie warum?
Eine Klasse, mehrfache Ausprägung<?phpclass PublishedPageExporter extends PageExporter { public function __construct() { $this->setPageDao(new PublishedPageDao()); }}
class WorkingCopyPageExporter extends PageExporter { public function __construct() { $this->setPageDao(new WorkingCopyPageDao()); }}
Dependency Injection – Was, wie warum?
"Only deleted code is good code!"Oliver Gierke
Eine Klasse, mehrfache Ausprägung
Dependency Injection – Was, wie warum?
Eine Klasse, mehrfache Ausprägung<?phpclass PageExporter { public function __construct(IPageDao $pageDao) { $this->pageDao = $pageDao; }}
Dependency Injection – Was, wie warum?
Eine Klasse, mehrfache Ausprägung<?xml version="1.0" encoding="UTF-8" ?><beans>
<bean id="ExportLive" class="PageExporter"><constructor-arg ref="PublishedPageDao" />
</bean>
<bean id="ExportWorking" class="PageExporter"><constructor-arg ref="WorkingCopyPageDao" />
</bean></beans>
Dependency Injection – Was, wie warum?
Eine Klasse, mehrfache Ausprägung<?php
// create ApplicationContext instance$ctx = new ApplicationContext();
// retrieve live exporter$exporter = $ctx->getBean('ExportLive');
// retrieve working copy exporter$exporter = $ctx->getBean('ExportWorking');
Dependency Injection – Was, wie warum?
Externe Services mocken
Dependency Injection – Was, wie warum?
Externe Services mocken
BookingmanagerBookingmanager WS-Konnektor
WS-Konnektor WebserviceWebservice
Dependency Injection – Was, wie warum?
Externe Services mocken
BookingmanagerBookingmanager WS-Konnektor
WS-Konnektor WebserviceWebservice
Dependency Injection – Was, wie warum?
Zur Erinnerung: Der Vertrag!
Externe Services mocken
BookingmanagerBookingmanager FS-Konnektor
FS-Konnektor FilesystemFilesystem
Dependency Injection – Was, wie warum?
Externe Services mocken
BookingmanagerBookingmanager FS-Konnektor
FS-Konnektor FilesystemFilesystem
erfüllt denVertrag!
Dependency Injection – Was, wie warum?
Sauberer, lesbarer Code
Dependency Injection – Was, wie warum?
Sauberer, lesbarer Code<?phpclass DeletePage extends Mvc_Action_AAction {
private $pageManager;
public function __construct(IPageManager $pm) {$this->pageManager = $pm;
}
protected function execute(Mvc_Request $request) {$this->pageManager->delete(
(int) $request->get('pageId'));
return new ModelAndView($this->getSuccessView());}
}
Dependency Injection – Was, wie warum?
Keine Framework Abhängigkeit
Dependency Injection – Was, wie warum?
Keine Framework Abhängigkeit<?phpclass MySampleService implements IMySampleService { private $sampleDao;
public function __construct(ISampleDao $sampleDao) {$this->sampleDao = $sampleDao;
}
public function getSample($sampleId) {try { return $this->sampleDao->readById($sampleId);}catch(DaoException $exception) {}
}}
Dependency Injection – Was, wie warum?
Dependency Injection – Was, wie warum?
Wie siehst das nun in der Praxis aus?
Dependency Injection – Was, wie warum?
Pimple
Pimple – Erste Schritte
Dependency Injection – Was, wie warum?
<?php
class TalkService {public function __construct() {}
public function getTalks() {}
}
Pimple – Erste Schritte
Dependency Injection – Was, wie warum?
<?phprequire_once '/path/to/Pimple.php';require_once '/path/to/TalkService.php';
// create the Container$container = new Pimple();
// define talkService object in container$container['talkService'] = function ($c) { return new TalkService();};
// instantiate talkService from container$talkService = $container['talkService'];
Pimple – Constructor Injection
Dependency Injection – Was, wie warum?
<?php
interface GenericRepository {public function readTalks();
}
class TalkRepository implements GenericRepository {public function readTalks() {}
}
class TalkService {public function __construct(TalkRepository $repo) {}
public function getTalks() {}
}
Pimple – Constructor Injection
Dependency Injection – Was, wie warum?
<?phprequire_once '/path/to/Pimple.php';require_once '/path/to/TalkService.php';
// create the Container$container = new Pimple();
// define services in container$container['talkRepository'] = function ($c) { return new TalkRepository();};$container['talkService'] = function ($c) { return new TalkService($c['talkRepository']);};
// instantiate talkService from container$talkService = $container['talkService'];
Pimple – Setter Injection
Dependency Injection – Was, wie warum?
<?php
class Logger {public function doLog($logMsg) {}
}
class TalkService {public function __construct(TalkRepository $repo) {}
public function setLogger(Logger $logger) {}
public function getTalks() {}
}
Pimple – Setter Injection
Dependency Injection – Was, wie warum?
<?phprequire_once '/path/to/Pimple.php';require_once '/path/to/TalkService.php';
// create the Container$container = new Pimple();
// define services in container$container['logger'] = function ($c) { return new Logger();};$container['talkRepository'] = function ($c) { return new TalkRepository();};$container['talkService'] = function ($c) { $service = new TalkService($c['talkRepository']); $service->setLogger($c['logger']); return $service;};
// instantiate talkService from container$talkService = $container['talkService'];
Dependency Injection – Was, wie warum?
Zend\Di – Erste Schritte
Dependency Injection – Was, wie warum?
<?phpnamespace Acme;
class TalkService {public function __construct() {}
public function getTalks() {}
}
Zend\Di – Erste Schritte
Dependency Injection – Was, wie warum?
<?php
$di = new \Zend\Di\Di();
$service = $di->get('Acme\TalkService');$service->getTalks();
Zend\Di – Constructor Injection
Dependency Injection – Was, wie warum?
<?phpnamespace Acme;
interface GenericRepository {public function readTalks();
}
class TalkRepository implements GenericRepository {public function readTalks() {}
}
class TalkService {public function __construct(TalkRepository $repo) {}
public function getTalks() {}
}
Zend\Di – Constructor Injection
Dependency Injection – Was, wie warum?
<?php
$di = new \Zend\Di\Di();
$service = $di->get('Acme\TalkService');$service->getTalks();
Zend\Di – Setter Injection
Dependency Injection – Was, wie warum?
<?phpnamespace Acme;
class Logger {public function doLog($logMsg) {}
}
class TalkService {public function __construct(TalkRepository $repo) {}
public function setLogger(Logger $logger) {}
public function getTalks() {}
}
Zend\Di – Setter Injection
Dependency Injection – Was, wie warum?
<?php$di = new \Zend\Di\Di();$di->configure(
new Zend\Di\Configuration(array(
'definition' => array('class' => array(
'Acme\TalkService' => array('setLogger' => array('required' => true)
))
))
));
$service = $di->get('Acme\TalkService');var_dump($service);
Zend\Di – Interface Injection
Dependency Injection – Was, wie warum?
<?phpnamespace Acme;
class Logger {public function doLog($logMsg) {}
}
interface LoggerAware {public function setLogger(Logger $logger);
}
class TalkService implements LoggerAware {public function __construct(TalkRepository $repo) {}
public function setLogger(Logger $logger) {}
public function getTalks() {}
}
Zend\Di – Interface Injection
Dependency Injection – Was, wie warum?
<?php
$di = new \Zend\Di\Di();
$service = $di->get('Acme\TalkService');$service->getTalks();
Zend\Di – Grundsätzliches
Dependency Injection – Was, wie warum?
<?php
$di = new \Zend\Di\Di();
$service = $di->get('Acme\TalkService');var_dump($service);
$service2 = $di->get('Acme\TalkService');var_dump($service2); // same instance as $service
$service3 = $di->get('Acme\TalkService',array(
'repo' => new \Acme\TalkRepository())
);
Zend\Di – Builder Definition
Dependency Injection – Was, wie warum?
<?php// describe dependency$dep = new \Zend\Di\Definition\Builder\PhpClass();$dep->setName('Acme\TalkRepository');
// describe class$class = new \Zend\Di\Definition\Builder\PhpClass();$class->setName('Acme\TalkService');
// add injection method$im = new \Zend\Di\Definition\Builder\InjectionMethod();$im->setName('__construct');$im->addParameter('repo', 'Acme\TalkRepository');$class->addInjectionMethod($im);
// configure builder$builder = new \Zend\Di\Definition\BuilderDefinition();$builder->addClass($dep);$builder->addClass($class);
Zend\Di – Builder Definition
Dependency Injection – Was, wie warum?
<?php
// add to Di$defList = new \Zend\Di\DefinitionList($builder);$di = new \Zend\Di\Di($defList);
$service = $di->get('Acme\TalkService');var_dump($service);
Dependency Injection – Was, wie warum?
Symfony2
Dependency Injection – Was, wie warum?
<?phpnamespace Acme\TalkBundle\Controller;use Symfony\Bundle\FrameworkBundle\Controller\Controller;use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class TalkController extends Controller { /** * @Route("/", name="_talk") * @Template() */ public function indexAction() { $service = $this->get('acme.talk.service'); return array(); }}
Symfony2 – Konfigurationsdatei
Dependency Injection – Was, wie warum?
<?xml version="1.0" ?><container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/serviceshttp://symfony.com/schema/dic/services/services-1.0.xsd">
</container>
File services.xml in src/Acme/DemoBundle/Resources/config
Symfony2 – Constructor Injection
Dependency Injection – Was, wie warum?
<?xml version="1.0" ?><container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd">
<services><service id="acme.talk.repo"
class="Acme\TalkBundle\Service\TalkRepository" />
<service id="acme.talk.service" class="Acme\TalkBundle\Service\TalkService">
<argument type="service" id="acme.talk.repo" /></service>
</services></container>
Symfony2 – Setter Injection
Dependency Injection – Was, wie warum?
<?xml version="1.0" ?><container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd">
<services><service id="acme.talk.logger"
class="Acme\TalkBundle\Service\Logger" />
<service id="acme.talk.repo" class="Acme\TalkBundle\Service\TalkRepository" />
<service id="acme.talk.service" class="Acme\TalkBundle\Service\TalkService">
<argument type="service" id="acme.talk.repo" /><call method="setLogger">
<argument type="service" id="acme.talk.logger" /></call>
</service></services>
</container>
Symfony2 – Setter Injection (optional)
Dependency Injection – Was, wie warum?
<?xml version="1.0" ?><container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd">
<services><service id="acme.talk.logger"
class="Acme\TalkBundle\Service\Logger" />
<service id="acme.talk.repo" class="Acme\TalkBundle\Service\TalkRepository" />
<service id="acme.talk.service" class="Acme\TalkBundle\Service\TalkService">
<argument type="service" id="acme.talk.repo" /><call method="setLogger">
<argument type="service" id="acme.talk.logger" on-invalid="ignore" />
</call></service>
</services></container>
Symfony2 – Property Injection
Dependency Injection – Was, wie warum?
<?xml version="1.0" ?><container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd">
<services><service id="acme.talk.repo"
class="Acme\TalkBundle\Service\TalkRepository" />
<service id="acme.talk.service" class="Acme\TalkBundle\Service\TalkService">
<property name="talkRepository" type="service" id="acme.talk.repo" />
</service></services>
</container>
Dependency Injection – Was, wie warum?
Flow3 – Constructor Injection
Dependency Injection – Was, wie warum?
<?phpnamespace Acme\Demo\Controller;
use TYPO3\FLOW3\Annotations as FLOW3;
/** * @FLOW3\Scope("session") */class StandardController extends \TYPO3\FLOW3\MVC\Controller\ActionController {
/** * @var \Acme\Demo\Service\TalkServiceInterface */protected $talkService;
public function __construct(\Acme\Demo\Service\TalkService $talkService) {$this->talkService = $talkService;
}
public function indexAction() {}
}
Flow3 – Setter Injection (manuell)
Dependency Injection – Was, wie warum?
<?phpnamespace Acme\Demo\Controller;
use TYPO3\FLOW3\Annotations as FLOW3;
/** * @FLOW3\Scope("session") */class StandardController extends \TYPO3\FLOW3\MVC\Controller\ActionController {
/** * @var \Acme\Demo\Service\TalkServiceInterface */protected $talkService;
public function setTalkService(\Acme\Demo\Service\TalkService $talkService) {$this->talkService = $talkService;
}
public function indexAction() {}
}
Flow3 – Setter Injection (manuell)
Dependency Injection – Was, wie warum?
File Objects.yaml in Packages/Application/Acme.Demo/Configuration# @package AcmeAcme\Demo\Controller\StandardController: properties: talkService: object: Acme\Demo\Service\TalkService
Flow3 – Setter Injection (automatisch)
Dependency Injection – Was, wie warum?
<?phpnamespace Acme\Demo\Controller;
use TYPO3\FLOW3\Annotations as FLOW3;
/** * @FLOW3\Scope("session") */class StandardController extends \TYPO3\FLOW3\MVC\Controller\ActionController {
/** * @var \Acme\Demo\Service\TalkServiceInterface */protected $talkService;
public function injectTalkService(\Acme\Demo\Service\TalkService $talkService) {$this->talkService = $talkService;
}
public function indexAction() {}
}
Dependency Injection – Was, wie warum?
Fokus aufs Wesentliche. Keine Ablenkung.
Dependency Injection – Was, wie warum?
Wiederverwendung steigern.
Dependency Injection – Was, wie warum?
Hilft den Code besser zu verstehen.
Dependency Injection – Was, wie warum?
Wieder Spaß bei der Arbeit ;)
Dependency Injection – Was, wie warum?
Kein Standard. Kein Tooling.
Dependency Injection – Was, wie warum?
Es braucht Zeit um DI zu verstehen.
Dependency Injection – Was, wie warum?
Konfiguration vs. Laufzeit
Vielen Dank!