Přejít k navigační liště

Zdroják » Různé » Dependency Injection: předávání závislostí

Dependency Injection: předávání závislostí

Články Různé

V dnešním pokračování seriálu o Dependency Injection si představíme různé varianty (Constructor injection, Setter injection, Interface injection či Property Injection), popíšeme jejich principy, zhodnotíme výhody a nevýhody, popíšeme dopady na kód, a pro srovnání si ukážeme i vzor Service Locator.

minulém díle seriálu jsme si na příkladu ukázali, proč bychom měli DI používat. Připomeňme si to sérií diagramů, kde jsou naznačené změny v závislostech našeho systému. Třída Assembler na obrázcích reprezentuje místo, kde se sestavují objekty do té podoby, ve které je chceme ve výsledku používat.

Z diagramů je patrné, že s tím, jak postupně zobecňujeme vztah mezi FooService a Cache, se požadavky na konfiguraci přesouvají na „klienta“ – v tomto případě objekt Assembler. To, jak se v Assembleru budou objekty sestavovat, je dáno zvoleným způsobem předávání závislostí do objektů. K dispozici je nám konstruktor, metody a properties objektů.

Varianty Dependency Injection

Constructor injection

Při použití Constructor Injection definujeme závislosti třídy v konstruktoru, a tím vyžadujeme, aby bylo předáno vše, co objekt potřebuje ke svému životu. Tento typ DI jsme použili také v minulém dílu seriálu.

class FooService {

    /** @var ICache */
    private $cache;

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

    ...

}

class FileCache implements ICache {

    /** @var string */
    private $dir;

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

    ...

}

class Assembler {

    public function createFooService() {
        return new FooService(new FileCache(TEMP_DIR . '/cache'));
    }

}

Setter Injection

Při použití Setter Injection využíváme pro předání závislostí metod, které jsou pojmenované podle konvence, tzv.  settery:

class FooService {

    /** @var ICache */
    private $cache;

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

    ...

}

class FileCache implements ICache {

    /** @var string */
    private $dir;

    public function setDir($dir) {
        $this->dir = $dir;
    }

    ...

}

class Assembler {

    public function createFooService() {
        $cache = new FileCache();
        $cache->setDir(TEMP_DIR . '/cache');
        $fooService = new FooService();
        $fooService->setCache($cache);
        return $fooService;
    }

}

Interface Injection

U Interface Injection budeme, podobně jako u Setter Injection, definovat metody pro předání parametrů do objektu, tentokrát ale jejich jméno určíme pomocí speciálních rozhraní:

interface InjectCache {

    public function injectCache(ICache $cache);

}

interface InjectDir {

    public function injectDir($dir);

}

Objekty, do kterých chceme závislosti předávat, tato rozhraní implementují:

class FooService implements InjectCache {

    /** @var ICache */
    private $cache;

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

    ...

}

class FileCache implements ICache, InjectDir {

    /** @var string */
    private $dir;

    public function injectDir($dir) {
        $this->dir = $dir;
    }

    ...

}

class Assembler {

    public function createFooService() {
        $cache = new FileCache();
        $cache->injectDir(TEMP_DIR . '/cache');
        $fooService = new FooService();
        $fooService->injectCache($cache);
        return $fooService;
    }

}

Property Injection

Místo použití metod bychom mohli používat i properties třídy. Při použití standardních OOP prostředků bychom ale všechny property, do kterých chceme zapisovat, museli definovat jako public. Ukázky by vypadaly velmi podobně jako u Setter Injection.

Pokud chceme properties ponechat definované jako privátní, můžeme pro dosažení cíle použít například reflexi. Tentokrát v ukázce nepoužijeme kód třídy Assembler, protože tento způsob DI bychom pravděpodobně při „manuálním“ sestavování objektů nevyužívali. Navíc je implementace silně závislá na zvoleném jazyku. Zápis ostatních tříd může vypadat např. takto:

class FooService {

    /**
     * @Inject
     * @var ICache
     */
    private $cache;

    ...

}

class FileCache {

    /**
     * @Inject
     * @var string
     */
    private $dir;

    ...

}

Dopady DI na kód

Dependency Injection by měl být pouze způsob, jak sestavovat objekty dohromady, měl by mít proto minimální požadavky na úpravu kódu (za předpokladu, že kód píšeme čistě). Největším porušením návrhu objektů je pravděpodobně využití Property Injection.

U verze s private properties z veřejného rozhraní třídy vůbec není zřejmé, jaké má závislosti (zdánlivě žádné). Při psaní aplikace to nemusíme pocítit – o závislosti se postará nějaké automatizované řešení, ale ve chvíli, kdy budeme chtít napsat např. unit test a předat namockované objekty, se dostaneme do úzkých.

Verze s public properties porušuje zapouzdření objektu. Kromě toho z rozhraní není jasné, které properties musíme inicializovat, aby objekt mohl vůbec fungovat. Stejný problém má i Setter Injection a Interface Injection. Ukažme si tento problém právě na Setter Injection:

class FooServiceTest extends TestCase {

    public function test() {
        $fooService = new FooService();   // konstruktor objektu nepožaduje žádné parametry
        $fooService->doSomethingUseful(); // způsobí PHP Fatal error: Call to a member function on a non-object
    }

}

Objekt tedy lže o svých závislostech – zdánlivě nic nepožaduje, ale při použití se pak dozvíme pravý opak a musíme se vrátit ke studiu rozhraní, případně kódu třídy, abychom zjistili, jaké všechny proměnné musíme před použitím inicializovat. Souvisejícím problémem je, že zveřejněné metody/properties může kdokoli volat opětovně, nemáme tedy nikde jistotu, že v půlce práce s objektem někdo nezmění hodnotu, která byla uvedena při inicializování.

Oba tyto problémy pak vedou ke konstrukcím uvnitř třídy, které kontrolují, zda byla hodnota už nastavena (např. kvůli čitelnějším výjimkám pro programátora než je Fatal Error), resp. jestli se ji nepokouší někdo změnit, když už jednou nastavena byla. U Interface Injection musíme ještě počítat s tím, že pro každou závislost, kterou plánujeme někam injectovat, musíme vyrobit samostatný interface, a ten pak v těchto třídách implementovat.

Constructor Injection netrpí ze své podstaty žádným z výše uvedených problémů – není vlastně možné třídu instancovat v nekonzistentní podobě, ani ji do ní později dostat. Constructor Injection má ale také své problémy. Pokud máme více způsobů, jak daný objekt sestavit, nemáme v PHP mnoho možností (např. v Javě lze používat overloading a vyrobit více různých konstruktorů) – pravděpodobně bychom se museli uchýlit k nepovinným parametrům konstruktoru, což může vést opět k situaci, kde není jasné, jaké kombinace parametrů musíme předat. Pro zpřehlednění je pak vhodné vyrobit např. factory třídu, nebo použít factory metody, které různé způsoby vzniku objektu pokryjí.

Navzdory výše zmíněnému riziku Constructor Injection preferuji, protože má nejmenší vliv na přirozené psaní tříd a poskytuje ochranu před nekonzistentním stavem. – pozn.aut.

Není také žádná povinnost si vybrat pouze jeden způsob, dost často to ani není možné. Především při používání kódu třetích stran (knihovny, frameworky) nemáme možnost ovlivnit jejich podobu. Tedy částečně máme, můžeme využít např. návrhového vzoru Adapter.

Service Locator

Pro srovnání s jednotlivými typy DI si ještě krátce představíme alternativní implementaci IoC – vzor ServiceLocator (SL). Stejně jako DI slouží ke snížení závislostí mezi požadovanou funkčností a konkrétní implementací. Pracuje tak, že máme nějakou třídu, která v sobě nese informaci o tom, jaké rozhraní je implementováno jakou třídou (tzv. kontejner). Pokaždé, když v nějaké třídě potřebujeme přistoupit k jiné, požádáme SL, aby nám vydal příslušný objekt. V našem příkladě by tedy u třídy FooService zmizela závislost na konkrétní implementaci cache, zůstala by závislost na rozhraní ICache, ale přibyla by závislost na rozhraní Service Locatoru (srovnejte s diagramy v úvodu článku).

Následuje ukázka, jak by mohl vypadat FooService v případě, že je Service Locator implementován jako třída se statickými metodami pro výdej objektů (opět silně zjednodušený příklad).

class FooService {

    /** @var ICache */
    private $cache;

    public function __construct() {
        $this->cache = ServiceLocator::getCache();
    }

    ...

}

class FileCache implements ICache {

    /** @var string */
    private $dir;

    public function __construct() {
        $this->dir = ServiceLocator::getCacheDir();
    }

    ...

}

class ServiceLocator {

    public static function getCache() {
        return new FileCache();
    }

    public static function getCacheDir() {
        return TEMP_DIR . '/cache';
    }

    ...

}

class Assembler {

    public function createFooService() {
        return new FooService();    // ale předpokládá, že je nějak nakonfigurovaný SL
    }

}

Rozdílem mezi oběma představenými implementacemi IoC je to, že v případě DI třída pouze deklaruje, která rozhraní potřebuje, a to, jakým způsobem a kdy je dostane, nijak neovlivňuje. Naopak u tříd využívajících SL vždy voláme kontejner na konkrétním místě. Velkou nevýhodou také je, že každá třída, která SL využívá, je závislá na jeho rozhraní (případně můžeme definovat částečná rozhraní pro jednotlivé služby, které SL vydává, ale závislost stejně zůstává). Pokud máme vytvořit nějaké hodně obecné třídy, tato závislost v nich zůstane, a třídy budou svázané s tímto kontejnerem.

Z hlediska dopadů na kód, které byly diskutovány v předchozích částech, je SL jednoznačně invazivnější než DI. S tím souvisí i čitelnost kódu – u Dependency Injection, zvláště pokud použijeme Constructor Injection metodu, vidíme zcela přesně všechny závislosti, které třída má, na první pohled. U Service Locatoru bychom pro dosažení stejného cíle museli prohlédnout celý kód třídy, protože volání Service Locatoru se může vyskytnout kdekoli v kódu (v ukázce je volán pouze v konstruktorech, to ale nemůžeme nikde zaručit).

V praxi se kromě „statického“ SL kontejneru můžeme setkat i s variantou, kdy je kontejner předáván do každého objektu, který má nějaké závislosti (bývá označen názvy jako Context nebo Container).

Závěr

Účelem dnešního článku bylo především zmapovat možné způsoby předávání závislostí do objektů a jejich srovnání z hlediska dopadu na kód. Dosud jsme mluvili o DI v teoretičtější rovině a všechny objekty jsme „manuálně“ skládali. To je v kontextu nasazení DI napříč celou aplikací neúnosné, a tak si v dalším díle ukážeme způsob, jak si tuto práci ulehčit.

Komentáře

Subscribe
Upozornit na
guest
34 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
View all comments
smilelover

Service Locator nema s IoC nic spolecneho, protoze porusuje zakladni premisu danou primo v samotnem nazvu! Nejde o inversion of control, kontrolu si ponechavam ja — ja si zadam o zavislost misto aby mi byla dodana pri stavbe objektoveho grafu. Stejne tak zcela evidentne neni mozne mluvit o „injection“…

SL je proste a jednoduse neco docela jineho a popravde je to spise takovy kriplik…

Jan Tichý

To není IMHO pravda. Když budu mluvit v rámci příkladu z článku, tak inverze obecně (ať už realizovaná pomocí DI, nebo pomocí SL) spočívá na principu, že o tom, která konkrétní implementace ICache se použije uvnitř FooService, se rozhoduje venku mimo FooService.

A to je splněno jak u DI (kde si FooService řekne o některou implementaci ICache ve svém konstruktoru, setteru nebo property), tak u SL (kde si FooService lokátoru „dej mi nějakou implementaci ICache“).

Akorát u DI to do ní zvenku i vstřiknu, proto „injection“. Zatímco u SL si to ta servisa vyzvedne sama. V obou případech ale určuji VENKU, mimo FooService, kterou konkrétní implementaci ICache ta FooService dostane. V obou případech se tedy jedná o princip IoC.

Service Locator skutečně je z mnoha pohledů nevhodný a nečistý návrh. Nejvíc u něj asi vadí právě ta závislost všech ostatních tříd na něm, která vnáší do aplikace zbytečnou komplexitu (a vše s tím související, jako je omezená přenositelnost takových tříd jinam, složitější testovatelnost apod.).

Na druhou stranu SL může mít oproti DI někdy i nezanedbatelné výhody. Zejména ve chvíli, kdy ho nevolám staticky, ale předávám si jeho instanci, tak můžu vesele využívat přepínání kontextu v různých částech aplikace. To je ale spíš okrajová záležitost…

alefo

To je akademicka debata, ale Fowler to chape v [1] ako dve alternativy, kde exaktne hovori, ze Dependency Injection je vec, kde trieda dostane instancie zvonku bez toho, aby si ich musela sama pozhanat a Service Locator je … well Service Locator. Zaroven konstatuje, ze existuju aj hybridne pristupy (Apache Avalon, nech mu je zem lahka).

[1] http://martinfowler.com/articles/injection.html

Aleš Roubíček

Odkazovat Fowlera v tomto případě není zrovna šťastné, přeci jen jsou jeho články na toto téma poněkud zastaralé.

Bohužel, seriál není o DI ale o IoC a pak jmenuje jednu z technik DI ServiceLocator, který je dnes považován za anti-pattern. Takže považuji za trochu nešťastné věnovat mu tolik místa a nezmínit, že jde o „zlo“.

Dále je dobré zmínit, že property injection a setter injection jsou de facto to samé, protože property je je pouhou abstrakcí nad gettery/settery. V článku je bohužel pod pojmem property injection ukázána injekce do fieldů pomocí reflexe, což je stejný anti-pattern jako SL.

Dále jsem v článku nenašel popis základního rozdílu mezi constructor injection a setter injection – a tou je ne/povinnost závislostí. Setter injection by se měla používat pro injektování nepovinných závislostí a nejlépe ve spojení se vzorem NullObject, který nám zajistí fungující objekt i bez provedené injektáže. V settru pak stačí pouhá kontrola, že není injektována reference na null.

„Chuck Norris si závislosti nevytváří, on je deklaruje.“

manik.cze

Seriál je orientovaný na DI, IoC a SL jsou zmíněny pouze jako související pojmy. SL je tu věnováno „tolik“ prostoru, protože myslím, že se to spoustě lidí plete (těch, co s DI zkušenosti nemají – pro ty je seriál koncipován). Více minimalisticky se mi to už popsat nepodařilo.

Sám SL za anti-pattern také považuji, ale do článku se mi to spíš psát nechtělo. Koneckonců používá to spousta frameworků, takže se s tím lidé prostě dříve nebo později setkají.

Ano je to to samé, ale vzhledem k tomu, že se setování přímo do public properties IMHO moc nepoužívá, tak jsem tomu nějak nevěnoval pozornost. Injekce pomocí reflexe je zde protože to je jedna z možností, s kterou pracují některé kontejnery a jako označují ji právě jako Property Injection (nebo se snad mýlím?).

Co přesně myslíš nepovinnou závislostí? Můžeš mi to prosím vysvětlit ideálně na nějakém konkrétním příkladu?

Jan Tichý

Nepovinná závislost – třeba logger. Když se injektuje, tak se loguje. Když se neinjektuje, tak se neloguje.

Aleš Roubíček

Nepovinné závislosti jsou závisloti, bez kterých objekt může fungovat. Jedním z příkladů budiž logování (které by mělo být implementováno pomocí AOP, ale ne všude je dostupné. Nebo je možné použít logovací dekorátor…)

Objekt bez logovací závislosti může vykonávat svou činnost, ale v případě, že je mu pomocí setter injection poskytnuta implemetace logeru, může navíc i logovat. NullLogger v tomto případě sice přijímá logovací zprávy, ale ve skutečnosti s nimi nic nedělá, jeho implementace je „prázdná.“

manik.cze

No já to chápu tak, že buď mám nějaký objekt, který tu závislost vyžaduje (třeba ten Dekorátor) a můžu mu předat NullLogger, nebo tam ta závislost není a nepředávám nikde nic.

To, že se jeden a ten samý objekt bude chovat jinak (vynechá/přidá) fukčnost podle toho, jestli jsem někde zavolal nějaký setter (nebo předal nepovinný parametr do konstruktoru) se mi moc nepozdává.

Aleš Roubíček

V případě užití vzoru *NullObject* si takovou nepovinnou závislost uspokojí objekt sám. Podobně, jako jsou reference inicializovány null hodnotou. V C# by kód mohl vypadat následovně:

class Foo {
  ILogger logger = NullLogger.Instance;

  public void Bar() {
    logger.Log("something");
  }

  public ILogger Logger {
    set { logger = value ?? NullLogger.Instance; }
  }
} 

V tomto případě nikdy nedojde k `NullReference­Exception` i když neobsahuje defenzivní kód.

manik.cze

Aha, no tam se mi nelíbí ta (zbytečná) další závislost na konkrétní třídě. To, jak tohle vidím já – závislost je vždy povinná, se potom zkombinuje s vhodnou konfigurací kontejneru – o nějaké implementaci řeknu, že je defaultní a ta se pak použije všude tam, kde není výslovně řečeno jinak. K `NullReference­Exception` taky nikdy dojít nemůže, rozhraní i chování je transparentní a nemusím kvůli tomu psát žádné speciální konstrukce do třídy.

Aleš Roubíček

ale tím vytvoříš z nepovinné závislosti povinnou. Rozdíl je v semantice.

Tady dávám veřejným rozhranním jasně najevo, jak je s objektem zamýšleno pracovat bez nutnosti to explicitně deklarovat na dalším místě (konfigurace kontejneru).

Ad závislost na konkrétní třídě. Tahle závislost je zcela bezpečná. Zaprvé její implementace nedělá vůbec nic (neobsahuje žádný globální stav ani nemá vedlejší efekty), je to jako mít závislost na `null`. Zadruhé se dá pomocí setter injection snadno nahradit jinou implementací `ILogger`.

manik.cze

Já ten rozdíl vidím a chápu. Nemám na to žádný vyhraněný názor, ale stejně si myslím, že by se třída od téhle „logiky“ okolo tohohle měla oprostit. Jí to má být úplně fuk, co jí kdo nastaví (a to samé má říkat svým rozhraním – setter imho znamená „můžeš mi to změnit kdykoli za běhu, mě je to jedno“). To, že děláš NullObject je pouze náhražka za to, že pak nemusíš psát všude dál v kódu if (logger != null), jinak je to to samé. V obou případech si ta třída uvnitř sebe defacto kontroluje zda k logování dojde nebo ne.

Když si vezmeš SRP, tak ten říká, že by v třídě nemělo být nic navíc, než co je nezbytně nutné. Pokud to nutné je, tak to používá a jde tedy o závislost, kterou by si měla nechat dodat. Pokud není, tak můžu použít např. zmiňovaný dekorátor.

NullObject je tedy podle mého právě vhodný použít čistě „zvenku“ – třída požaduje nějaké rozhraní a my víme, že nechceme aby dělala tu jednu část, kterou nepotřebujeme, takže to je spíš takový „hack“.

To jak se tam ten NullObject dostane pak už záleží na okolní aplikaci – pravděpodobně kontejner. A pak už záleží na tom jaké schopnosti konfigurace kontejner má – jak moc to bude transparentní nebo opruz psaní. Jak jsem psal – napříč celou aplikací prohlásím, že např. EchoLogger je můj defaultní (a ten se pak autowiringem doplňuje na ta místa, kde je potřeba daný interface). Pokud ale v některých (tady pozor – co znamená to některých – kde a kdo to bude rozlišovat jestli se to tam setne nebo ne?) případech budu chtít jiné chování, tak si řeknu, že tahle služba Foo se předá jinak nakonfigurovaná (s NullLoggerem) – ale to pak musím někde stejně explicitně napsat.

Další věc, která je způsobená tímhle je, že ten NullObject musí mít buď statický přístup k instanci (jako v tvojí ukázce) – tzn. pro mě by to např. znamenalo tenhle přístup ve všech těch NullObjectech vyrobit (+ Foo je na tomhle přístupu závislá). Alternativou je samozřejmě použít new, ale to by se vytvářely pořád dokola zbytečné objekty…Pokud konfiguruju zvenku můžu předávat pořád jednu a tu samou instanci, aniž bych k ní něco statického musel dopisovat.

Ještě otázka – doteď jsme mluvili o nepovinných závislostech pouze s výchozím NullObjectem. Používáš to pouze v téhle kombinaci nebo i pro určení defaultní implementace, která něco má opravu dělat (aby opět bylo naznačeno jak je s objektem zamýšleno pracovat)?

Aleš Roubíček

NullObject ale není defaultní implementace, je to neimplementace. :) Proto není nic špatného na tom, mít jí tam tímto způsobem zadrátovanou. Jak říkám, je to jako null, ale je silně typový a referenčně bezpečný.

To, že tam ty defenzivní ify nejsou, je právě tou velikou výhodou. V implementaci se zaměřuješ na opravdovou logiku a neřešít mezní případy, nezvyšuješ cyklomatickou komplexitu…

manik.cze

Ano, samozřejmě! O samotných NullObjectech a jejich funkci nepolemizuju. Zajímaly mě ty ostatní věci.

Aleš Roubíček

To z toho vychází. :) V ostatních případech to přípustné není, protože pak už pracuješ s implementací, která může mít vedlejší efekty a to není žádoucí.

Nox

To, že děláš NullObject je pouze náhražka za to, že pak nemusíš psát všude dál v kódu if (logger != null), jinak je to to samé.
Myslim že je lepší lehce zvýšit komplexitu implementace než náročnost použití… přidáš si jeded až pár ifů a už pak nikde nemusíš vytvářet dummy logger
To co píšeš zde ten setter skutečně znamená
(mj. pokud bude argument povinný, tak si třída přece uvnitř sebe NEkontroluje jestli logovat bude nebo ne… to by určil uživatel předáním dummy loggeru nebo skutečného, ale to je off topic)

A navíc souhlasím s tím, že jde prostě o jinou sémantiku, pokud je závislost v konstruktoru, třída říká že nutně potřebuje ke svému chodu logger. Pokud je setterem, říká jejímu uživateli, že je logger nepovinný … i když chápu že toto už je možná trochu subjektivní

Nox

Aha, přehlédl jsem ten snippet s inicializací nullloggeru

Michal Augustýn

Možná by to bylo čitelnější/pocho­pitelnější, kdyby tam bylo něco takového:
ILogger logger = NullObject<ILog­ger>.Instance;

martin

„property injection ukázána injekce do fieldů pomocí reflexe, což je stejný anti-pattern“
zaujimali by ma dovody, preco je to antipattern – mozes o tom nieco viac napisat, pripadne dat odkaz na clanok/clanky?

Aleš Roubíček

Protože to
1. zjevně porušuje zapouzdření
2. porušuje pricip Inversion of Control, objekt nedeklaruje své závislosti navenek, ale má přímou silnou závislost na infrastrukturním kódu (anotace). Jde o deklarativní service lokátor, kód rozhodně není lepší.

Michal Kára

Pěkně to má vyřešení framework Spring – používá injection do anotovaných properties (které mohou být private). Člověk jen napíše

@Autowired
private Trida promenna;

a je to :-)

Constructor injection jsem taky používal, ale tam je u složitějších případů potřeba ručně vyřešit závislosti – které objekty vytvářet nejdřív, protože jsou potřeba jako parametry při vytváření dalších objektů.

Jan Tichý

„tam je u složitějších případů potřeba ručně vyřešit závislosti – které objekty vytvářet nejdřív, protože jsou potřeba jako parametry při vytváření dalších objektů.“

To přece vůbec nesouvisí jenom s constructor injection. U úplně každého typu injektáže si člověk může závislosti řešit ručně, nebo to za sebe nechat dělat nějaký kontejner. A popravdě je to drbání pravou rukou za levým uchem, pokud využívám DI bez kontejneru, který za mě automaticky vytváří všechny třídy, sám řeší a hlídá všechny závislosti včetně správného pořadí apod. O DI kontejnerech bude tuším příští díl tohoto seriálu.

alefo

Mate nejake negativne skusenosti s rucnym riesenymi zavislostami v Constructor Injectione v Springu?

Ved tam sa to vyriesi automaticky, ci nie?

Michal Illich

Tak si vyberte, je to ručně nebo automaticky? :)

uživatel Springu

Ve Springu je řešení závislostí automaticky. Výjimkou jsou situace kdy závislosti jsou „divné“, Spring je nemá šanci poznat, a vývojář je musí popsat extra – v XML jde o atribut depends-on. V každém DI containeru který má za něco stát to samozřejmě musí být automaticky, jinak se z toho vývojář zblázní. Prostě jednou už vývojář dal najevo, že objekt A má být předán jako parametr objektu B, takže z toho plyne jednak že objekt A se musí vytvořit (tranzitivně včetně všech svých závislostí) před objektem B, a na konci zase že objekt B se bude rušit dříve než objekt A.

alefo

Ja som to chápal tak, že construction injection v Springu je rovnaký ako setter injection, teda že okrem „divných“ prípadov netreba riešiť nič špeciálne (strom závislostí medzi beanmi si Spring vyrobí automaticky.)

So setter injection som s tým nikdy nemal pri malých projektoch problém a keďže constructor injection nepoužívam, nie je mi jasné, čo tým myslí pán Kára.

X

Ještě lépe má DI pomocí anotací vyřešeno Google Guice. Ohledně toho constructor injection jste musel něco dělat špatně, tam řešení závislostí funguje naprosto automaticky, právě z toho důvodu že jde o konstruktor a tedy má container jasně dané, které objekty dělat dřív a které později.

Michal Kára

Sorry, trochu jsem pomíchal dvě věci dohromady. Pokud na tím mám framework na řešení dependencies, tak to samozřejmě já řešit nemusím.

seberm

Už se moc těším na další díl.

Gaudentius

Jakým zpsůobem naložit se závislostmi, poku chci využít „type hinting“?

classA
{

public function __construct(Za­pisovac $zaposivac)
{

}

}

new classA(new Zapisovac());

Ondřej Mirtes

V čem je problém?

Gaudentius

No pokud jsem z článku pochopil správně, tak principem DI je zbavit třídu závislostí na jiných třídách?!

Pokud tedy v třídě A chci instancovat třídu B, předám v třídě A třídě B konstruktorem závislosti/instance jiných tříd, které potřebuji aby třída B měla k dispozici.

Pokud ale chci v třídě B použít „type hinting“, tak defakto již do třídy B závislost přenáším, protože v „type hinting“ definuji o proti čemu se má kontrolovat:

class A
{
public function neco() { $a = new B( new C(); ) }
}

class B
{
public function __construct(C $c) { $thtis->c = $c }
}

V tride A instancuji tridu B a do tridy B pomoci „construct injection“ predavam instanci tridy C.

Snad jsem to vysvětlil a nebo pokud v rámci DI není „tyho hinting“ problém, tak to motám?

Aleš Roubíček

V type hintu by se měla objevit pouze abstrakce (base class, interface).

Ondřej Mirtes

V DI zbavuješ obracíš vztah k závislostem (inversion of control) – namísto, aby sis je vytvářel či sháněl sám (operátor new či sahání do nějakého globálního registru), necháš si je do objektu injektnout zvenčí.

Namísto konkrétní implementace si můžeš vyžádat interface nebo abstraktní třídu, ale to je detail. Jde hlavně o to, že neřešíš, jak nakonfigurované objekty ti přicházejí, to řeší ten, kdo ten objekt instanciuje.

Kompozice objektů je jeden ze základních pilířů OOP a toho se samozřejmě zbavovat nechceš :)

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.

Pocta C64

Za prvopočátek své programátorské kariéry vděčím počítači Commodore 64. Tehdy jsem genialitu návrhu nemohl docenit. Dnes dokážu lehce nahlédnout pod pokličku. Chtěl bych se o to s vámi podělit a vzdát mu hold.