Dependency Injection

03/2015 / Milan "perún" Herda / @moriquend / hrad.perunhq.org

O čom budeme hovoriť

  • Čo je Dependency Injection
  • typy Dependency Injection
  • DI container
  • Service Locator

Čo je Dependency Injection?

  • implementácia princípu Inversion of Control
  • zvyšuje modularitu
  • robí kód rozšíriteľným
  • zvyšuje znovupoužiteľnosť

Čo to je IoC?

Inversion of Control (IoC) je princíp, kedy sa uprednostňuje kontrola ZVONKU objektu nad situáciou, kedy si tento objekt sám hovorí, čo v rámci svojho kódu potrebuje.

Keď objekt volá metódy iného objektu, hovoríme, že na ňom závisí.

Objekt by nemal svoje závislosti vytvárať vo svojom tele

Objekt by mal svoje závislosti dostať od niekoho, kto ho využíva.

A toto je to magické DI

Odovzdávanie závislostí objektu zvonka

Čo je cieľom?

  • viditeľné závislosti
  • objekty v stabilnom stave

Toto sú pravidlá, ktoré je potrebné mať stále na pamäti.

Ukradnutá ukážka


$a = new A;

$b = new B;
$b->hello();
                    
Ukradnutá z Grudlovho článku o DI

Typy DI

Property injection


class A
{
    /** @var CacheInterface */
    public $cache;
}

//...

$cache = new Cache();
$a = new A;
$a->cache = $cache;
                    

Property injection nie je dobrá

  • nie sú viditeľné závislosti
  • keď sa nenastaví property, objekt je v nestabilnom stave

Setter injection


class A
{
    /** @var CacheInterface */
    private $cache;

    public function setCache(CacheInterface $cache)
    {
        $this->cache = $cache;
    }
}

//...

$cache = new Cache();
$a = new A;
$a->setCache($cache);
                    

Setter injection nie je dobrá

  • nie sú viditeľné závislosti
  • závislosti stratené medzi obyčajnými setter metódami
  • keď sa nezavolá setter, objekt je v nestabilnom stave

Setter injection pomocou inject metódy


class A
{
    /** @var CacheInterface */
    private $cache;

    public function injectCache(CacheInterface $cache)
    {
        $this->cache = $cache;
    }
}

//...

$cache = new Cache();
$a = new A;
$a->injectCache($cache);
                    

Setter injection cez inject metódu je lepšia, ale nie je dobrá

  • + závislosti sú viditeľnejšie
  • - keď sa nezavolá inject, objekt je v nestabilnom stave

Constructor injection


class A
{
    /** @var CacheInterface */
    private $cache;

    public function __construct(CacheInterface $cache)
    {
        $this->cache = $cache;
    }
}

//...

$cache = new Cache();
$a = new A($cache);

                    

Constructor injection je najlepšia

  • závislosti je vidieť hneď v konštruktore
  • objekt je hneď po vytvorení v stabilnom stave
  • nič nám tento stabilný stav nevezme

$a->__construct($anotherCache);
                    

Toto nám vezme náš stabilný stav.

Zavolať takýto kód môže hocikto, kto má prístup k $a

Nevadí, je to sviňárna a povedzme, že to nikto neurobí.

Takže: constructor injection!

Kde sa budú vytvárať objekty?

V továrničkách a v DI kontajneri

DI container

DI container

  • obsahuje v sebe všetky služby systému
  • je zodpovedný za ich vytváranie, alebo úzko prepojený na ich vytváranie

všetky služby = veľa

Nie je časťou runtime logiky!

  • startup proces
  • runtime logika

Separation of concerns

Konfigurácia

Definícia služieb poskytovaných DIC sa často nepíše ako PHP kód, ale ako konfigurácia

  • yaml
  • neon
  • xml

Lazy inicializácia

Služby sa zväčša vytvárajú až vo chvíli, keď ich niečo chce použiť.

Pimple

http://pimple.sensiolabs.org/
  • Príklad implementácie malého, ale silného DIC
  • Využívaný napr. v Silexe

Service Locator

Service Locator je iné použitie DI containeru.

Do objektu sa vloží celý DIC namiesto jednotlivých závislostí

Objekt si potom závislosti vyťahuje z containeru podľa potreby

To znie dobre, nie?

Problém so Service Locatorom

Nevidíme závislosti!

Objekt sa môže dostať do nekonzistentného stavu

Ďakujem za pozornosť

Otázky?