Dependency Injection je zřejmé předávání závislostí, tedy že se každá třída otevřeně hlásí ke svým závislostem, místo toho, aby je někde pokoutně získávala. Co kdyby se závislosti předávaly přímo do proměnných? Proberu úskalí a výhody property injection.
Property injection má jednu podstatnou výhodu: stručnost. Srovnejte:
class Foobar
{
/** @var HttpRequest */
private $httpRequest;
/** @var Router */
private $router;
function __construct(HttpRequest $httpRequest, Router $router)
{
$this->httpRequest = $httpRequest;
$this->router = $router;
}
}
versus
class Foobar
{
/** @var HttpRequest @inject */
public $httpRequest;
/** @var Router @inject */
public $router;
}
Proměnné musíme definovat tak jako tak. Zpravidla u nich uvádíme
i anotaci @var
a příslušný datový typ. Je lákavé si
ušetřit práci a místo psaní rutinního kódu konstruktoru nebo metody
inject()
doplnit prosté @inject
. Property injection
kromě minimálního režijního kódu navíc parádně řeší problém s předáváním
závislostí a dědičností.
Použití anotace představuje jinou konvenci pro předání závislostí. Zdůrazňuji slovo jinou, protože ať už vyjmenujeme závislosti jakožto argumenty metody nebo anotováním, jde o ekvivalentní činnost. Čímž oponuji názoru, že použití anotace představuje závislost na kontejneru. To v žádném případě není pravda, jde jen o konvenci, koneckonců dosud o kontejnerech nepadla řeč a ukázky dávají smysl.
Stejně tak se nedívejte na anotaci @inject
jako nějakou odpornou
magii, kterou musíte nastudovat, abyste ji mohli používat. Žádná magie
tu není. Jde o obyčejné veřejné proměnné a anotace je jen doplňující
informace pro programátora, říkající, že objekt vyžaduje tyto proměnné
naplnit. (Nutno dodat, že Jakub Vrána reagoval na použití anotací
u private proměnných, což magie je.)
V článku o předávání závislostí jsem se používání proměnných širokým obloukem vyhnul, protože mají vážné nedostatky:
- public proměnné nezajistí typovou kontrolu
- public proměnné nezajistí neměnnost
- private proměnné nelze naplnit žádnou jazykovou konstrukcí
- private proměnné nejsou součástí veřejného API – nejde tedy o deklaraci závislosti!
- pro protected proměnné platí nevýhody obou
Ještě bych přidal, že anotace nejsou nativní součástí jazyka PHP a jde tedy o nestandardní konvenci, oproti třeba injektáži přes konstruktor.
Poznámka: vstřikování závislostí do privátních proměnných posvětila třeba Java EE 6 a je to skutečně ee. Třída své závislosti tají (private = neveřejný) a nelze ji instancovat jinak, než kontejnerem (závislost na kontejneru). Jde zcela proti smyslu Dependency Injection, jak je popsán v perexu tohoto článku, a také proti základnímu principu OOP, zapouzdření. Označil bych to jako „Inversion of Dependency Injection.“
Pro properly property injection bychom potřebovali once-write-only veřejnou proměnnou s typovou kontrolou. Kdyby tohle PHP umělo, nic by nebránilo je používat. Jenže PHP to neumí.
Emulace inject property
PHP to neumí, ale lze to emulovat!
Emulaci zajistíme pomocí magických metod __set
a
__get
. Jak ale dosáhnout toho, aby se k public proměnné
přistupovalo skrze tyto metody? Použijeme trik: v konstruktoru ji
unsetneme. Proměnná zmizí a při přístupu k ní se již použijí
magické metody.
Příklad implementace ve formě základní třídy Object by mohl vypadat třeba takto:
class Object
{
private $injects = array();
function __construct()
{
// následující analýza proměnných by se mohla kešovat
$rc = new ReflectionClass($this);
foreach ($rc->getProperties() as $prop) {
if ($prop->isPublic() && strpos($prop->getDocComment(), '@inject')
&& preg_match('#@var\s+(\S+)#', $prop->getDocComment(), $m)
) {
// unset property to pass control to __set() and __get()
unset($this->{$prop->getName()});
$this->injects[$prop->getName()] = array('value' => null, 'type' => $m[1]);
}
}
}
function __set($name, $value)
{
if (!isset($this->injects[$name])) {
throw new Exception("Cannot write to an undeclared property $$name.");
} elseif ($this->injects[$name]['value']) {
throw new Exception("Property $$name has already been set.");
} elseif (!$value instanceof $this->injects[$name]['type']) {
throw new Exception("Property $$name must be an instance of {$this->injects[$name]['type']}.");
} else {
$this->injects[$name]['value'] = $value;
}
}
function __get($name)
{
if (!isset($this->injects[$name])) {
throw new Exception("Cannot read an undeclared property $$name.");
}
return $this->injects[$name]['value'];
}
}
pak stačí deklarovat výše uvedenou třídu Foobar
jako
potomka Object
a vše bude fungovat standardně podle
očekávání:
class Foobar extends Object
{
/** @var HttpRequest @inject */
public $httpRequest;
/** @var Router @inject */
public $router;
}
$fb = new Foobar;
$fb->router = new Router;
Navíc však máme zajištěnou neměnnost a typovou kontrolu:
$fb->router = new Router;
// Exception: Property $router has already been set.
$fb->httpRequest = new Router;
// Exception: Property $httpRequest must be an instance of HttpRequest.");
Čistá cesta nebo prasárna?
Zkusme se zamyslet nad tím, co vlastně anotace @inject
představuje: hint pro programátora, že proměnnou má při vytváření
objektu nastavit a že ji později nesmí měnit. Anotace @var
pak
nařizuje typ.
Je na programátorovi, aby dodržel kontrakt. Stejně jako v případě
anotace @private
v PHP 4 nebo JavaScriptu,
či anotace @return
v současném PHP. Jde o pravidla, u nichž
se předpokládá, že je programátor dodrží, aniž to lze na úrovni
interpreteru ověřit.
Třída Object rozšiřuje PHP o schopnost kontroly za běhu, usnadní tedy identifikaci chyb. Je to vychytávka navíc. Z mého pohledu tedy akceptovatelná cesta k použití property injection v PHP. Možná by se dalo uvažovat nad zařazením do Nette\Object a legitimizace této injektáže v Nette.
Všechny části:
- Co je Dependency Injection?
- Dependency Injection versus Service Locator
- Dependency Injection a předávání závislostí
- Dependency Injection a property injection (právě čtete)
- Dependency Injection versus Lazy loading
Komentáře
maryo #1
Je to trochu prasárna, ale IMO legitimní, přijde mi to nejvíc user friendly z těch možností co jsou. Už jsem tenhle „trik“ s unsetem někde použil, ale tam to moc legitimní nebylo :).
maryo #2
#1 maryo, I když… Už se mi to zas tak nelíbí. Hlavně kvůli tomu, že je potřeba vytvořit reflexi pro každej novej objekt, to to radši předám do konstruktoru, zas tolik práce to neni ne? Jsem zvědavej, co na to zkušenější.
David Grudl #3
#2 maryo, Předpokládám, že by se analýza proměnných kešovala. Příklad jsem tím ale nechtěl komplikovat.
maryo #4
#3 Davide Grudle, No ale pořád se budou jednou vytvářet a procházet reflection vlastnosti všech prvních instancí tříd dědících z Nette\Object i když pravděpodobně velká většina z nich vůbec @inject používat nebude :/. Přijde mi to jako velká daň i když by to možná chtělo udělat benchmark.
jasir #5
Moc pěkné, opravdu… Po implementaci cachování (musel jsem vyzkoušet) je overhead přibližně 10× větší než u prázdného konstruktoru, což je relativně ok.
Nápad – o takhle k anotaci @inject přidat ještě @required? Stejně jako @inject slouží jako informace pro programátora, a ušetřili bychom psaní kódu pro kontrolu naplněnosti povinných závislostí?
Ad. přidání do Nette\Object – jsem rozhodně pro, ale problém by mohlo být, že lidé nejsou zvyklí volat v třídách rozšiřujících Nette\Object konstruktor parentu … alespoň já to tak nedělám…
David Grudl #6
#4 maryo, zda vůbec a jak by to bylo implementované v Nette\Object neřeším, tohle je proof of concept bez optimalizací.
#5 jasire, jaký by byl rozdíl mezi @inject a @required? Možná by bylo lepší použít @required namísto @inject.
Jinak s tím konstruktorem v Nette\Object je komplikace i v tom, že by jej nebylo možné přepsat s nižší viditelností. Nejlepší by bylo najít nějaké řešení pro PHP 5.4 využívající traits.
jasir #7
#6 Davide Grudle, To je pravda, rozdíl by nebyl, nějak jsem nepřemýšlel.
ad. Nette\Object – co varianta udělat chování volitelné přes nějaké volání $this->forcePropertyTypeChecking(). Plus mít možnost kvůli tuto vlastnost vypnout přes Nette\Object::$forcePropertyTypeChecking = false pro produkční mód ?
Jan Dolecek #8
@required a @optional by mohlo slouzit jednak jako informace pro programatora, ze bez nejake zavislosti trida nemuze (resp. muze) fungovat. Znovupouzitelna komponenta ArticleCloud muze potrebovat ArticleRepository, ale Translator mit jen volitelny – pokud jej v aplikaci mam, tak jej pouziju pro lepsi poteseni uzivatele. Pokud nemam, tak to uzivatel nejak zvladne.
Podobne treba optional zavislost na Cache/CacheStorage.
Navic by tomu mohl rozumet DIContainer a usnadnit tim autowirovani. Pokud kontejner znal translator, tak by ho komponente rovnou predal, kdyz by ji vytvarel.
Michal Gebauer #9
Takže, už zbývá vyřešit jen kontrolu, jestli je model/objekt připravený?
Ohledně povinnosti značené pomocí slova required pak jako normální by měl být optional namísto inject, protože inject nevyjadřuje volitelnost. Nebo
@inject required
jako sousloví. Popřípadě@inject @required
. Což by bylo asi nejčitelnější.Honza #10
Je to spíš prasárna než čisté řešení. Já bych se spokojil s tím, že konstruktoru předám objekt např. Properties $properies, který obsahuje reference na potřebné objekty typu Router a HttpRequest. Tento objekt $properties pak může mít metodu na kontrolu požadovaných objektů typu HttpRequest a Router a metoda vyhodí výjimku, pokud v $properties nebudou nastaveny.
David Grudl #11
Než
@inject optional
bych raději zvolil@var HttpRequest|null
.#10 Honzo, chybí ti tam veřejná deklarace závislostí. Něco, z čeho by bylo patrné, jaké objekty se požadují.
Honza #12
#11 Davide Grudle, Bez té deklarace bych se klidně obešel, protože některé objekty můžou být povinné, jiné volitelné a některé i mezi sebou závislé, takže nějaká pevná deklarace nemusí mít ani smysl. Uvedl bych ty závislosti do komentáře ke konstruktoru, protože to mi při psaní kódu hodí hint, v případné dokumentaci to také bude a při spuštění se vždy provede kontrola (ta může být pro různé objekty různě složitá). Má to nějaké mínusy? Pokud ano, tak bych stejně raději použil první uvedenou možnost s vyjmenováním v konstruktoru.
David Grudl #13
#12 Honzo, potom jde o service locator, kterému se právě snažím vyhnout.
Jakub Tesárek #14
Jsem z toho rozpačitý. Chápu, že tenhle článek nemá srovnávat property injection s jinými způsoby, ale stejně mi to nedá. Proč se nedržet constructor injection?
Aleš Roubíček #15
Constructor a Property injection jsou zcela validní techniky DI a mají dokonce svou semantiku, kterou
@inject
tak trochu porušuje.Injektovat by měla jít jakákoliv vlastnost, která má setter. Závislosti předávané pomocí setterů by měli být nepovinné – tj. takové, které objekt pro svou činnost nepotřebuje nutně. Naproti tomu konstruktorem by se měly injektovat závislosti povinné.
Ty tvrdíš, že
@inject
nesouvisí s kontejnerem, ale pak je tu stejně ukázka s reflexí… Programátor pozná kde má injektovat díky setteru. Anotace je pro stroj a proto si za svým tvrzením stále stojím. :)David Grudl #16
#14 Jakube Tesárku, proč nepoužít konstruktor injection? Tady a tady jsem se snažil dát obšírnou odpověď, proč to za určitých okolností v praxi nejde, lépe to napsat neumím a opakovat se nechci.
#15 Aleši Roubíčku, s druhým odstavcem zcela souhlasím. A protože existují situace, kdy konstruktor, který preferuji, nelze z technických důvodů použít (viz odkazy), musí být nahrazen, třeba metodou inject() nebo anotací @inject.
Je to ekvivalent uvedení závislosti jakožto parametru konstruktoru. Jiná forma téhož, syntaktický detail. Ukaž příklad, ve kterém to neplatí.
(Použití reflexe se týkalo vnitřní implementace třídy, workaroundu jazykového omezení, s kontejnerem nesouvisí absolutně).
Vena #17
Tak toto je snad největší blbost, kterou jsem tě kdy viděl napsat.
Očividně o Javě EE nic moc nevíš. Musíš si uvědomit, že Java EE neni framework, ale platforma, buď ji budeš používat (a budeš mít výhody jako je anotace @Inject), nebo nebudeš (a budeš používat „pouze“ Javu SE).
Jinak máš pravdu, jde zcela proti smyslu DI, tak jak je popsán v perexu tvého článku. Naštěstí je anotace @Inject součástí specifkace CDI (což je jedna ze specifikací Javy EE 6), které tvou filozofii DI, kdy se třída otevřeně hlásí ke svým závislostem, nesdílí.
Tuto poznámku jsis fakt mohl odpustit, děláš ze sebe ignoranta.
David Grudl #18
#17 Veno, prosím, už dost, „posvětila“ !== „nutí“, přečti si to znovu bez emocí, další komentáře o private property injection budu jako správný ignorant mazat ;)
Aleš Roubíček #19
#16 Davide Grudle, Problém s dědičností lze vždy vyřešit kompozicí. Proto si nemyslím, že „musí být nahrazen.“ Nemusí. :)
David Grudl #20
#19 Aleši Roubíčku, chápu to tedy jako souhlas 🙂
Jakub Tesárek #21
#16 Davide Grudle, Tak jsem asi tenhle článek špatně pochopil. Tohle se týká výlučně presenterů? Ok, v nette se presentery dostaly dostaly do konstror hell. Stále si myslím, že nejlepším řešením by bylo zrefaktorovat presentery, nechat z presenteru jen inteface, pokud to jen trochu půjde tak se vyhnout dědění (nejen u presenterů ale obecně). Problem solved.
Každopádně pokud se jedná o to, jak umožnit pěkné DI presenterů bez potřeby je zásadně refaktorovat, pak souuhlasím, constructor injection není optimální.
David Grudl #22
#21 Jakube Tesárku, v článku nic o presenterech nepadlo, proč by se jich měl týkat? Řeší čistě problematiku property injection, její výhody a nevýhody. Property injection jsem si nevymyslel, pouze jsem o tom napsal článek.
Jakub Tesárek #23
#22 Davide Grudle, Odkázal jsi mě na články, ve kterých měly argumenty proti mému tvrzení, že constructor injection je lepší. Nenašel jsem tam ale obecné argumenty, pouze vysvětlení, proč se nehodí tobě pro presentery v nette. Proto jsem se vrátil a vyptával se dál.
A samozřejmě vím, že sis property injection nevymyslel, není mi deset 🙂
Ale teď když ten článek čtu znovu, tak koukám, že jsem si jednu nebo dvě věty nějak domyslel z tvého článku o předávání závislostí presenterům a tak jsem sám sebe zmátl. Tímto se ti omlouvám
Vena #24
#18 Davide Grudle,
Máš pravdu, myslel jsem, že narážíš na to, že @Inject se nedá použít mimo Javu EE. Omlouvám se za trošku arogantní příspěvek.
Každopádně nechápu, proč se navážíš do Javy EE, když s ní nemáš žádou zkušenost.
David Grudl #25
#23 Jakube Tesárku, preferuji constructor injection, protože jde o nejsilnější konvenci mající oporu v jazyce. Obecné argumenty „proti“ jsem sepsal tady. (Píšu v uvozovkách, protože to nejsou argumenty proti, spíš jde o případy, kdy se stane constructor injection problematický). Tento článek v podstatě spíš před property injection v PHP varuje.
#24 Veno, To není navážení, ale věcná kritika jedné featury. Můžeš jí klidně oponovat, ale ne ad hominem, tj. poukazováním na mé (ne)zkušenosti. Taková argumentace je pak demagogická.
lopata #26
#16 Davide Grudle,
To tedy není, když volám konstruktor je hned jasné, co potřebuje předat. Když to udělám špatně, PHP zařve ještě před voláním konstruktoru.
Naproti tomu v téhle podivnosti není jasné z konstruktoru nic, nikdo nezařve, konstruktor se zavolá a k chybě dojde až při použití property.
To jsou dost podstatné rozdíly a určitě se tedy nejedná o ekvivalentní chování.
Nehledě na podstatné snížení výkonu a nafouknutí paměťových nároků při nějaké cachování. Tohle tedy určitě ne.
David Grudl #27
#26 lopato, psal jsem to v souvislosti se závislostí na kontejneru, v čemž si jsou ekvivalentní, protože žádnou závislost nemají. Jednotlivé injektáže pochopitelně ekvivalentní nejsou, jsou spíše diametrálně odlišené (cca 3 odstavec).
Kdo dál?
Vena #28
#25 Davide Grudle,
Ok, nechtěl jsem to už moc rozvádět, ale mám čas.
Nechápu, jak toto můžeš brát jako závislost na kontejnéru. Třída na něčem závisí, pokud je v ní nekde explicitně importovaná nebo používaná nějaká extérní komponenta.
Ale ty to očividně bereš nějak jinak. Asi chceš říct, že té třídě musí injektovat nějaké závislosti kontejnér, protože normální kostrukcí to nelze.
Toto odvozujěš na základě toho, že je potřeba kontejnér a třída je tedy na něčem extérním závislá, což by z principu DI být neměla.
Jenže stejně tak bude ta třída závislá např. na standardní knihovně jazyka. To podle tebe neporušuje DI?
Neporušuje, a já ti řeknu proč – DI není nějaký kontrakt, který třída musí dodržet, aby to byla DI-třída. DI je jenom návrhový vzor, který použiješ, když chceš některé závislosti vytáhnout ze třídy pryč. Nikdo ti neříká, aby jsi je vytáhnul pryč všechny, jinak nebude tvoje třída DI-třídou a nebude tedy tak cool. Nic jako porušení DI neexistuje.
Obecně vytáhneš ze třídy ty závislosti, kterým by jsi mohl v budoucnu nebo při runtime chtít změnit implementaci. Toto je motivace DI, je to napsané i na wikipedii:
Takže závislost na kontejnéru nejde proti smyslu DI – pokud se rozhodneme používat nějaký kontejnér, nemyslím si, že ho budeme chtít vyměnit – stejně jako nebudeme chtít vyměnit třeba nette, je to sice závislost (presentery jsou závislé na nette), ale to nám přece vůbec nevadí, mi to nette používáme, používat chceme a používat budeme.
Jak jsem mnohokrát říkal, DI obecně porušuje zapouzdření.
Já mám prostě pořád pocit, že DI někdo z nás nechápe.
Z diskuze s tebou mi příjde, že DI chápeš jako nějaký kontrakt, konkrétně viz perex.
Já chápu DI jako návrhový vzor, který je spíš definovaný svou motivací.
David Grudl #29
#28 Veno,
Přesně tak! A nevyjádřil jsem se přesně, zavislá je na injektoru. Je to moment, kdy jazyk popírá sám sebe, zkus se nad tím filosoficky zamyslet.
ad druhý odstavec o smyslu Dependency Injection: jak to popisuješ, DI vůbec nevnímám. Vnímáme ho stejně, dle mého ho ale private property injection kompletně porušuje. Proč? Protože neumožňuje závislosti vůbec nastavit (normální konstrukcí, pochopitelně) a ani je deklarovat (privátní prvky se do veřejných API referencí vůbec neuvádí). Čímž se zase dostáváme k závislosti na injektoru.
Já jsem zase mnohokrát říkal, že ne. Tady asi budeš potřebovat lepší argument 🙂
Vena #30
#29 Davide Grudle,
No, jak jsem říkal, kontejnér nelze brát jako závislost, která by mě obtěžovala, stejně tak standardní knihovna. Ale toto ti asi nevysvětlím, když DI tak jak ho popisuju nevnímaš :)
Myslel jsem, že to stačilo ve vedlejší diskuzi. Tak tedy znova. Závislosti jsou implementační detail. A imlemntační detaily by neměli být vidět skrz API. Další věc jě, že nějaká třetí strana musí vědět o závislostech mojích tříd. Diskuze na stackoverflow, kterou jsem odkazoval ve vedlejší diskuzi, to docela slušně objasňuje.
bene #31
#30 Veno, Tak jinak. Řekněme, že napíšeš nějakou knihovnu, závislou na onom injektoru.
Pak tu knihovnu dáš k dispozici.
Někdo ji bude chtít použít, ale používá jíný injektor nebo žádný, prostě chce jen vytvořit instanci.
V tom případě musí člověk použít vždy tvůj/obecný injektor. V prvním případě má dva v druhém něco, co vůbec nechtěl používat.
Nevím těď jak je to v java EE, pokud má nějakou přímou podporu pro @inject a SE ne. Ale pokud je to tak, a pokud napíšeš závislost na EE @inject, tak už to nepoužiješ v SE. To mi příjde jako obrovské omezení.
Pokud ale použiješ standartní jazykové konstrukce pro injektování závislostí, tak nemusíš žadný kontejner/injektor použít a instanci si vytvoříš sám.
DI a kontejnér neznamená totéž. DI je jen technika/doporučení, jak předat závislosti. Kontejnér je jen usnadnění, pro vytváření instancí objektů, které mohou, ale nemusí získávát své závyslosti technikou DI.
Z toho jasně plyne, že private a předávání závislostí pomocí kontejneru je porušení DI. Protože objekt je „nepřímo“ závislý na onom injektoru.
lopota #32
#27 Davide Grudle, OK, takže ekvivalentní je property injection a konstruktor injection tedy jen v tom, že jde o DI.
Pořád je tady to zhoršení výkonu a navýšení paměťové náročnosti u implementace. Když by se něco mělo někam zavádět, tak určitě bez těchto brzd. Celá implementace v nějakém frameworku by stačila tak, aby nějaký něco umělo vytvořit objekt a nastavit mu závislosti dle property injection bez toho, aniž by bylo nutné dědit od nějakého božského objektu. Mnohem raději se smířím s tím, že to nebude typově bezpečné atd., jsem v dynamickém programovacím jazyku a tam nic takového jako „once-write-only veřejnou proměnnou s typovou kontrolou“ nečekám, takže nechci, aby mi jí někdo nutil.
Vena #33
#31 bene,
Pokud napíšeš knihovnu, která závisí na jiné knihovně, tak je logické, že klientský kód bude potřebovat obě knihovny, tak to funguje úplně všude.
Java EE – Pokud napíšeš nějaký kód pro nette, těžko budeš chtít, aby to bylo kompatibilní třeba se Zendem. Java EE ja navíc mnohem víc, než jen framework.
Pozn.: Ve skutečnosti můžeš anotaci @Inject používat i mimo prostředí Javy EE, stačí ti opatřit nějaký DI kontejnér, který běží standalone, např. hojně používaný Spring.
Nechápeš DI. Neslouží k tomu, aby jses zbavil závislostí (to je nemožné). Slouží k odstranění těsných vazeb a nahrazení jich volnými vazbami (viz loose coupling vs tight coupling). Ty závislosti, tak jak je prezentuješ ty a David tam budou vždycky, musí existovat nějaká třetí strana, která mi ty závislosti dodá.
David Grudl #34
#32 lopoto, no tak to nepoužiješ, viz poslední odstavec článku. Jak prosté milý Watsone 🙂
#33 Veno, nezabředávej stále do vysvětlování DI, v tom není spor. Problém je v tom, že u PPI musí existovat injektor, bez kterého jsi namydlený. Sám jsi psal, že DI je jenom návrhový vzor a neexistuje nic jako DI-třída. No a najednou tu máš třídy, které jsou DI, přesněji DIC-only, protože bez injektoru jsou nepoužitelné.
DI jako návrhový vzor mohu používat nezávisle na jazyku, zatímco PPI je něco tvrdě svázaného s „frameworkem“.
Taktéž privátní proměnné nejsou vidět v API dokumentaci, takže ani nevím, že tam nějaké závislosti jsou. (Namítneš, že to vědět nemusím, že to ví injektor? A jsme u toho, závislost na injektoru! Ale to pak není DI.) Už se opakuju, diskuse v kruhu nemá smysl, viď?
Vena #35
#34 Davide Grudle,
Diskuze v kruhu je opravdu velmi únavná :)
Já mám právě pocit, že v tom je ten největší problém, podle mě tvoje definice DI z perexu je značne nepřesná.
Až budeš mít někdy čas, zkus se podívat na tento článek. Je to od Martina Fowlera.
Tady bych vypíchnul jen to, že takto vypadá klasický schéma problému, které se snaží DI řešit:
[* https://martinfowler.com/…es/naive.gif *]
Problém je v tom, že třída MovieLister je přímo závislá na implementaci rozhraní MovieFinder.
Řešení:
[* https://martinfowler.com/…injector.gif *]
Zbavili jsme se přímé závislosti na implementaci rozhraní MovieFinder! Hurá. Ale cosik nám tam přibylo. Říká se tomu různě, např. injektor. Toto schéma je nezávislé na tom, jakou techniku DI zvolíme! Ať je to DI přes konstruktor nebo přes PPI.
Toto je DI, tak jak tomu rozumím já, Fowler (jedna z největších Java EE mastermind) a snad celá Java komunita.
Pokud mi nevěříš a nevěříš ani Fowlerovi, tak si myslím, že tuto diskuzi můžeme ukončit a dále se netočit dokola :)
David Grudl #36
#35 Veno, Fowler má pravdu. A ano, diskusi můžeme ukončit.
jirka d. #37
Vzhledem k tomu, že jsem se podobné property injection pokoušel nad Nette implementovat před cca dvěma měsíci, ale bez toho čupr triku s unsetem v konstruktoru, takže to bylo implementačně trochu k ničemu :) dávám za sebe plus bod pro property injection. Má to i výhodu v tom, že se relativně jednodušše dají všechny závislosti vizuálně zobrazit, dále to při čtení kodu vypada dost dobře :) Drobny problem odstranitelny zcasti kesovanim, je akorat ta vykonnost, co se prasarny tyce: i nektery low-level veci v kernelech modernich operacnich systemu ukryvaji o dost vetsi praseciny a nikdo za to programatory nevesi na zapaleny krize cestou do programatorskeho Rima…proste se to podle me jednodusejc efektivne napsat neda…mozna cesta do budoucna je implementace DI pres traits (sice nemam zadnou predstavu, jak by to melo aspon mlhave vypadat, ale co…:) ) jenze kdy bude bezne na hostingu dostupna 5.4 v odladeny verzi (typicky 5.x.3 a vyssi)? To muze trvat taky par let…
bene #38
#33 Veno, A to je právě ono. Pokud napíši v Nette knihovnu která používá contructor/setter/public property pro nastaven9 zavislostí, můžu ji pak používat v Zendu a přes Zendovský konterjner si budu instance této knihovny vytvářet. Stejně tak naopak. Kdežto při nutné závislosti na konkrétním injektoru budu muset kopírovat i ten injektor.
Stejně tak v JAVE, když napíši knihovnu pomocí POJO tříd, tak ji mohu použít kdekoliv a zvolit si libovolný kontejner.
Ohledně závislostí, asi jsem se špatně vyjádřil. Samozřejmě že se jich nezbavíš, ale každá třída se k nim veřejně hlásí (to beru jako princip DI). Pokud bereš jako součást každé knihovny i konkrétní injektor, ok
Pokud někdo brere DI jako návrhový vzor, ok. Pro mě je to jen princip že se třída k závislostem veřejně hlásí a nezískává je nějakým jiným způsobem. Jako návrhový vzor bych pak mohl označit i to: Nepoužívejte public property, ale settery, protože tak můžete v budoucnu např. validovat délku řetězce. Pokud samozřejmě jazyk neumožňuje property jako např. C#.
Každý to vidíme trochu jinak, zůstaneme u toho 😉
Ondra #39
Óóó, krása… ještě dva tři roky a fakt přejdeš na Javu, kde tyhle jsou tyhle věci leta :)
Tento článek byl uzavřen. Už není možné k němu přidávat komentáře.