[2011–05–05] Finalizace Dependency Injection
- Honza Marek
- Člen | 1664
HosipLan napsal(a):
Když je nějaké služba, která nepotřebuje (čti nesmí) vidět všechny služby, tak je vhodnější vytvořit novou instanci
Container
u, předat do ní jen pár služeb a tu pak předat jako „kontext“ dané službě.
To je krásný antipattern, který je v Nette hojně využívaný. Samozřejmě hezčejší by bylo, kdyby ta služba měla do sebe předané přes konstruktor nebo settery už přímo potřebné služby nebo nějaké továrničky na ně (kdyby bylo potřeba línou inicializaci).
- Filip Procházka
- Moderator | 4668
Kdyby to bylo na mě, tak bych všem nutil jednu instanci a popř. předával jenom konkrétní služby. Jenom vysvětluju jak to teď funguje :)
- xificurk
- Člen | 121
HosipLan napsal(a):
Když je nějaké služba, která nepotřebuje (čti nesmí) vidět všechny služby, tak je vhodnější vytvořit novou instanci
Container
u, předat do ní jen pár služeb a tu pak předat jako „kontext“ dané službě.
„Tohle“ mi právě moc nejde do hlavy (vytvářet nový objekt jen proto,
abych do něj zkopíroval odkazy na služby z nějakého globálního
kontejneru). Protože jednak z obecného hlediska (zaměnitelnosti
implementací) nevím, jaké všechny služby bude ten konkrétní objekt, co
zrovna vytvářím, potřebovat; druhak, pokud si nějaká implementace bude
chtít šáhnout na nějakou službu, o které jsem původně usoudil, že ji
nepotřebuje/nesmí na ni šahat, tak toho stejně nakonec nějak dosáhne…
jen trochu krkolomněji. Prostě izolace jednotlivých služeb je tak trochu
dvousečná zbraň.
Další věc je, že většina služeb jsou typicky singletony (v rámci celé
aplikace) a jejich jedinečnost se dost blbě hlídá ve chvíli, kdy začnu
tvořit nové kontejnery pro jednotlivé služby.
Čímž rozhodně nechci tvrdit, že obecně více kontejnerů nemá smysl –
rozhodně mě napadají místa, kde se skvěle uplatní: více různých
připojení do databáze/kešování na různá místa, testování… jen moc
nevidím ten zde tolik opěvovaný přínos.
- Filip Procházka
- Moderator | 4668
Jestli jsem špatně pochopil, jak to TEĎ funguje v Nette, tak mě opravte. Samozřejmě jsem zastáncem jednoho containeru, pro celý životní cyklus Application a služby související (fakt nemám sílu cokoliv obhajovat), protože v tomhle dělení vidím jenom přínos pro omezení počtu parametrů v konstruktoru za cenu dvou containerů.
Kde mi dává smysl více Containerů, pak tady Doctrine\Container, ale to je zase trošku jiná pohádka..
Beztak se teď David doma chytá za čelo a říká si, že jsme to zase nepochopili :)
- Ondřej Mirtes
- Člen | 1536
Je to přesně tak. Pokud nějaká služba přijímá kontejner (svůj „kontext“), tak nevím, čím ho mám vlastně naplnit a musím jít zkoumat její zdrojáky. Vím, že jsme toto řešení do Nette s Mediem prosazovali, ale v té době se vše řešilo přes Environment, takže to byl krok kupředu. Je potřeba jít ale ještě dál :)
Správné řešení je explicitní vyjádření konkrétních závislostí tím, že je služba přijímá v konstruktoru, případně pro jednorázové použití v metodách.
(Setter injection je blbost a berlička pro případ, že konstruktor je už něčím obsazen. Tohle dělá špatně např. třída RobotLoader, která je závislá na CacheStorage, ale přijímá ho pomocí setteru a ne konstruktoru.)
Kontejner by měl být přístupný pouze na pár speciálních místech – napadají mě cron skripty, testy a PresenterFactory.
Doufám, že se na tohle David chystá.
- Patrik Votoček
- Člen | 2221
Mě smysl dávají „sub-kontejnery“ právě třeba na připojení k DB.
Co se týká aplikačního kontejneru tak ten vidím jako takovou náhradu za constructor/setter/getter injection. Aneb neposíláte přímo služby ale jejich kontejner.
- David Grudl
- Nette Core | 8218
Honza Marek napsal(a):
To je krásný antipattern, který je v Nette hojně využívaný.
Hojně = 1× v Nette\Application\Application?
Samozřejmě hezčejší by bylo … nebo nějaké továrničky na ně (kdyby bylo potřeba línou inicializaci).
A jak by taková implementace předávání továrniček místo služeb vypadala? Třeba jako současný DI\Container? ;-)
xificurk napsal(a):
Protože jednak z obecného hlediska (zaměnitelnosti implementací) nevím, jaké všechny služby bude ten konkrétní objekt, co zrovna vytvářím, potřebovat;
To právě vědět musíš. Ať už služby předáváš konstruktorem nebo přes settery, výčet služeb je jasný, jde o sumu všech parametrů.
Čímž rozhodně nechci tvrdit, že obecně více kontejnerů nemá smysl – rozhodně mě napadají místa, kde se skvěle uplatní: více různých připojení do databáze/kešování na různá místa, testování… jen moc nevidím ten zde tolik opěvovaný přínos.
Přínos to má pouze pro ty, kteří to chtějí využívat (a vidí v tom přínos). Z hlediska běžného a většinového uživatele se vůbec nic nemění. Pokud jsi dosud nad DI v Nette neuvažoval, nemusíš ani teď, ono pod kapotou běží zcela automaticky.
- Patrik Votoček
- Člen | 2221
David Grudl napsal(a):
Hojně = 1× v Nette\Application\Application?
Nette\Http\User
:-)
- David Grudl
- Nette Core | 8218
Ondřej Mirtes napsal(a):
Je to přesně tak. Pokud nějaká služba přijímá kontejner (svůj „kontext“), tak nevím, čím ho mám vlastně naplnit a musím jít zkoumat její zdrojáky.
V tom případě vytváříš služby špatně, co ti na to mám říct.
Vím, že jsme toto řešení do Nette s Mediem prosazovali, ale v té době se vše řešilo přes Environment, takže to byl krok kupředu. Je potřeba jít ale ještě dál :)
Ale kdepak, problém byl v $presenter = new $presenterClass
v Application, což vyřešil Honzův PresenterFactory.
(Setter injection je blbost a berlička pro případ, že konstruktor je už něčím obsazen. Tohle dělá špatně např. třída RobotLoader, která je závislá na CacheStorage, ale přijímá ho pomocí setteru a ne konstruktoru.)
Naprostý nesmysl. Setter injection zčitelňuje kód.
Kontejner by měl být přístupný pouze na pár speciálních místech – napadají mě cron skripty, testy a PresenterFactory.
Tak konkrétně: kde všude v Nette vadí kontejner?
- David Grudl
- Nette Core | 8218
Patrik Votoček napsal(a):
Nette\Http\User
:-)
Jenže tam se zcela záměrně předávají ty továrničky.
Mě by vážně zajímalo, jak by Honza s Ondrou přepsali celé DI v Nette k obrazu svému. Asi by nahradili container v Application za constructor setter (změna na 5 řádků s čistě akademickým smyslem a nulovým přínosem) a pro Nette\Http\User by vyrobili nějaké řešení, které by bylo tak podobné na DI\Container, že by to při dalším refaktoringu sloučili. No a jakmile vznikne potřeba přidat lazy službu do Application (což je v plánu), tak by zase revertli Application na kontejner. Tedy by došli ke stávající podobě. Ale jestli se v něčem šeredně mýlím, opravte mě.
Ve současné implementaci chybí jen jediné: chybějící volání
checkServiceType
v kontruktorech Application a User.
- Honza Marek
- Člen | 1664
David Grudl napsal(a):
Ondřej Mirtes napsal(a):
Je to přesně tak. Pokud nějaká služba přijímá kontejner (svůj „kontext“), tak nevím, čím ho mám vlastně naplnit a musím jít zkoumat její zdrojáky.
V tom případě vytváříš služby špatně, co ti na to mám říct.
Jak mi tedy vysvětlíš, že když jsem chtěl zjistit, které služby
potřebuje Nette\Http\User
, musel jsem se podívat do
zdrojáku? :)
- Honza Marek
- Člen | 1664
Proč jsou vlastně ty authorizator a authenticator líný? Jaký je rozdíl mezi instanciací těchto dvou a nějakýho containeru?
- xificurk
- Člen | 121
David Grudl napsal(a):
xificurk napsal(a):
Protože jednak z obecného hlediska (zaměnitelnosti implementací) nevím, jaké všechny služby bude ten konkrétní objekt, co zrovna vytvářím, potřebovat;
To právě vědět musíš. Ať už služby předáváš konstruktorem nebo přes settery, výčet služeb je jasný, jde o sumu všech parametrů.
…nebo přes spešl kontejner jako např. Nette\Http\User
,
že?
- Patrik Votoček
- Člen | 2221
David Grudl napsal(a):
Ve současné implementaci chybí jen jediné: chybějící volání
checkServiceType
v kontruktorech Application a User.
A tagy :-)
- Ondřej Mirtes
- Člen | 1536
David Grudl napsal(a):
V tom případě vytváříš služby špatně, co ti na to mám říct.
Špatně? Jak mám zvenku odhadnout, jaké má ty služba závislosti, když přijímá pouze DI kontejner? (který takhle zaujímá pozici ServiceLocatoru).
Ale kdepak, problém byl v
$presenter = new $presenterClass
v Application, což vyřešil Honzův PresenterFactory.
Tohle je obecný problém jakéhokoli místa, kde je operátor
new
. Jakmile je někde v kódu natvrdo new
, nijak na
tom místě tu napevno vytvářenou implementaci ničím nenahradíš. Proto
jsem navrhoval,
že by celé Nette mělo vnitřně fungovat na konfigurovatelném DI kontejneru,
což nyní částečně supluje pole $defaultServices
.
To, jak PresenterFactory zpříjemnila práci, značí, že by se z toho měl stát obecný princip pro všechno ve frameworku. Protože chci mít možnost cokoli nahradit vlastní implementací a vědět, kam kvůli tomu mám sáhnout.
Naprostý nesmysl. Setter injection zčitelňuje kód.
Může ho zčitelnit, pokud má služba 150 závislostí. To je ale bad code smell a říká si o refaktoring. Do počtu 3–4 parametrů v konstruktoru je to v pohodě. Problém se setterem je jeho volitelnost. Jak chceš uživateli říct, aby po vytvoření objektu zavolal ještě sadu setterů? Tohle vede k vývoji metodou pokus-omyl. „Hurá, zavolal jsem setCacheStorage a už to jede“. Ale co když tam je ještě druhý setter, jehož nezavolání se projeví až někde na serveru za ostrého provozu (třeba kvůli tomu, že daná vlastnost se využije jen při isProduction nebo za určité konstelace hvězd).
Setter by měl skutečně být volitelný, tzn. objekt by měl fungovat plnohodnotně bez něj. Nedoplním do RobotLoaderu CacheStorage? Fajn, jeho obsah se nebude kešovat, ale jinak bude plnohodnotně fungovat. RobotLoader se neobejde bez CacheStorage? Tak šup do konstruktoru, protože existence toho objektu nedává bez cache smysl.
Kontejner by měl být přístupný pouze na pár speciálních místech – napadají mě cron skripty, testy a PresenterFactory.
Tak konkrétně: kde všude v Nette vadí kontejner?
Viz ta první odpověď. Tímhle užitím děláš z kontejneru service locator. Škoda, žes nebyl na přednáškách Miška Heveryho, tam to moc pěkně vysvětlil a ukázal (prezentované poučky jsou pěkně shrnuté v této sadě článků, doporučuji). Jde o to, že tím, že objekt příjímá service locator lže o svých závislostech. Prostě se z toho nijak nedozvím, co ve skutečnosti potřebuje a čím mám tedy service locator naplnit. Důkazem budiž tento dotaz. Proč kód samotného frameworku vyhazuje Fatal Error, když jsem splnil vše, co po mně chtěl?
Editoval Ondřej Mirtes (15. 5. 2011 22:12)
- Ondřej Mirtes
- Člen | 1536
Cokoli má být lazy a uvnitř objektu, do kterého je to vstřikované, se
nevyužije vždy, to bych řešil pomocí továrniček. Takže bych do usera
předal AuthorizatorFactory
a AuthenticatorFactory
,
které by volaly new Authorizator
, resp.
new Authenticator
až při $factory->create()
.
Co se týče Usera, tak bych z něj autorizaci i autentizaci vyhodil úplně, protože mi tam nedává smysl. Musím si do něj předat autentikátor jenom kvůli tomu, aby na něm user zavolal nějakou metodu, kterou na něm mohu zavolat sám. Po Userovi požaduji jen to, aby mi zajistil přihlášení usera, tedy uložení jeho identity do session. Vše ostatní by měly zařizovat jiné objekty.
Porovnejte tyto dva kódy:
$user->setAuthenticationHandler($facebookAuthenticator);
$user->login($uid); // zde se zadem volá FacebookAuthenticator, je to nečitelné
a:
$identity = $facebookAuthenticator->authenticate($uid);
$user->login($identity); // zde se do session uloží to, co tam sám předám
Editoval Ondřej Mirtes (15. 5. 2011 22:20)
- redhead
- Člen | 1313
Ondřej Mirtes napsal(a):
Může ho zčitelnit, pokud má služba 150 závislostí. To je ale bad code smell a říká si o refaktoring. Do počtu 3–4 parametrů v konstruktoru je to v pohodě. Problém se setterem je jeho volitelnost. Jak chceš uživateli říct, aby po vytvoření objektu zavolal ještě sadu setterů? Tohle vede k vývoji metodou pokus-omyl.
Nemůžu než souhlasit. Tesat do monitoru.
- David Grudl
- Nette Core | 8218
Ondřej Mirtes napsal(a):
Cokoli má být lazy a uvnitř objektu, do kterého je to vstřikované, se nevyužije vždy, to bych řešil pomocí továrniček. Takže bych do usera předal
AuthorizatorFactory
aAuthenticatorFactory
, které by volalynew Authorizator
, resp.new Authenticator
až při$factory->create()
.
A jak bys zajistil, že takto vytvořený autorizátor bude singleton v nějakém scope, tj. budeš ho chtít předat ne do jednoho objektu User, ale do více objektů?
Špatně? Jak mám zvenku odhadnout, jaké má ty služba závislosti, když přijímá pouze DI kontejner? (který takhle zaujímá pozici ServiceLocatoru).
Pokud není možné předávat explicitně služby a trváš na předávání kontejneru (zdůrazňuji, že volba je na tobě) můžeš použít typehint a předat statický kontejner. V případě dynamického lze závislosti uvést v dokumentaci a kontrolovat pomocí checkServiceType.
Tohle je obecný problém jakéhokoli místa, kde je operátor
new
. Jakmile je někde v kódu natvrdonew
, nijak na tom místě tu napevno vytvářenou implementaci ničím nenahradíš. Proto jsem navrhoval, že by celé Nette mělo vnitřně fungovat na konfigurovatelném DI kontejneru, což nyní částečně supluje pole$defaultServices
.
Ale tak to přece funguje odjakživa, celé Nette je konfigurovatelné
pomocí systémového kontejneru, ten je konfigurovatelný pomocí
config.neon
.
A pokud tím celé Nette myslíš jako odstranit úplně
všechny new
, tak sorry, ale předávat v konstruktoru seznam
tříd výjimek, abych měl full-DI i třeba na výjimku
InvalidArgumentException, tak to je nesmyslná úchylárna.
Pokud jsi narazil na konkrétní případ, kde by bylo vhodné nahradit new za DI, tak ho konkrétně zmiň, jinak je to prázdné třepání.
Do počtu 3–4 parametrů v konstruktoru je to v pohodě.
Jasně, $obj = new Object($argX, $argB, $arg7, $argXy)
je
úžasně srozumitelné, jasné jak facka, zatímco
$obj->setTemplateFilter($argX)->setCacheStorage($argB)...
je
bad smell, fuj, špatně, ostuda, Miško Hevery se obrací v posteli ;)
Tak konkrétně: kde všude v Nette vadí kontejner?
Ptám se znovu, kde ti vadí? Našel jsi 1 případ, třídu User? Hrůza, tak to je celý framework na odpis! :-)
- David Grudl
- Nette Core | 8218
Už začínám být zoufalý z těch neustálých řečí, počínaje „Nette nemá DI“, přes „všechno je blbě, bad smell, hojný antipattern“ až po „v Nette to musíš dělat tak a ne jinak.“ To přece není vůbec žádná pravda! Chjo.
Navíc se tu formují dvě protichůdné skupiny, jedni měsíce naříkají, proč má Application jiný kontext než Environment, druzí kontranaříkají, že vůbec má nějaký kontext…
Takže:
- pokud vytváříte objekt, předávejte mu jeho závislosti jako instance konkrétní objektů (via konstruktor nebo přes settery)
- teprve když to vážně nebude možné, vytvořte statický kontejner (potomek DI\Container)
- nad ničím dalším neuvažujte, neřešte, nepřepínejte ;-) na DI se takřka nic nemění a bude fungovat stejně transparentně, jako dosud.
- VasekPurchart
- Člen | 20
Davide asi chápu kam tím míříš – nechceš asi nikomu nařizovat jak má kdo svůj kód přesně psát (vynucení constructor injection, použití kontextu jako Service Locator), chápu to správně? Jestli ano, tak s tím jednoznačně souhlasím, ale zároveň si taky myslím, že Nette samotné by mělo být napsáno tak, aby reprezentovalo ty nejčistší možné volby. – Constructor Injection a důkladné vyjmenovávání služeb (ne kontext/container/w/e).
Dobrým příkladem je právě RobotLoader, jak uváděl Ondra, s tím mám osobní zkušenosti.
- Cifro
- Člen | 245
Tak ako by som sa potom zbavil tohto
Environment::getCofing()
? (Ide o triedu z tutoriálu
z Kuchařky.) Potrebujem do služby dostať údaje z configu.
Služba sa definuje v config.neon
service:
ModelLoader: Proj\ModelLoader
Podľa prvého bodu ma to nenapadá, a pri druhom treba urobiť aj tovarničku napr. ako tuto – tomu by som sa chcel vyhnúť.
Sice je to dosť lamerský dotaz, ale možno nebude naškodu aj pre ostatných vidieť nejakú ukážku ako to urobiť s týmto „novým“ DI.
Editoval Cifro (15. 5. 2011 23:54)
- Ondřej Mirtes
- Člen | 1536
Pokud chci nějaký objekt mít vytvořený lazy a zároveň singleton, tak ta továrnička může vytvářet/vracet jedinou instanci.
class SingletonAuthenticatorFactory
{
private $instance;
public function create()
{
if ($this->instance == NULL) {
$this->instance = new Authenticator();
}
return $this->instance;
}
}
Konstruktor vs. setter:
Jak mám vědět, že po konstruktoru musím volat ještě
setTemplateFilter
a setCacheStorage
? Navíc bych
čitelně napsal new Object($cacheStorage, $templateFilter)
a
nešifroval bych to do $argX
proměnných :)
config.neon:
Tady jsem navrhoval jen to, že by se mi líbilo, kdyby
$defaultServices
bylo nahrazeno nějakým nette.neon
přímo ve frameworku, aby pro tu samou věc neexistovala dvojí syntaxe. Do
tohoto configu by se pak mergovaly uživatelské configy, ve kterých by se
mohla ta nastavení překrýt.
new
:
To samozřejmě nemůžeš aplikovat na výjimky, ty nahrazovat nechceš :)
Já vůbec neříkám, že je Nette na odpis, ale že ten mnou prezentovaný přístup mi přijde správný a univerzální, tak bych ho rád sjednotil napříč celým frameworkem. Proč Template nedostává celý kontejner, ale jen Translator setterem? Proč jiné objekty dostávají celý kontejner? Chci zabránit těmto situacím, ke kterým, jakmile si něco z jádra Nette sestavuji ručně (Application, Presenter, Control), dochází často.
- David Grudl
- Nette Core | 8218
manik napsal(a):
Davide asi chápu kam tím míříš – nechceš asi nikomu nařizovat jak má kdo svůj kód přesně psát (vynucení constructor injection, použití kontextu jako Service Locator), chápu to správně?
Přesně tak. A když už by se mě někdo zeptal, tak bych mu řekl, že service locator má zvolit jako krajní možnost.
Jestli ano, tak s tím jednoznačně souhlasím, ale zároveň si taky myslím, že Nette samotné by mělo být napsáno tak, aby reprezentovalo ty nejčistší možné volby.
Jsem přesvědčen, že tak tomu (limitně) je.
- Constructor Injection a důkladné vyjmenovávání služeb (ne kontext/container/w/e).
Dobrým příkladem je právě RobotLoader, jak uváděl Ondra, s tím mám osobní zkušenosti.
Má zkušenost zase říká, že setter injection vede k lépe čitelnému kódu, příklad jsem tu uvedl. Ale dejme tomu, že je to diskutabilní. Najdeš jiný případ krom RobotLoaderu, zmíněného User (kde zatím nikdo neuvedl ekvivalentní jiné řešení) a Application? Z těch všech řečí mám totiž pocit, že takových příkladů jsou tam desítky a desítky.
- David Grudl
- Nette Core | 8218
Ondřej Mirtes napsal(a):
Pokud chci nějaký objekt mít vytvořený lazy a zároveň singleton, tak ta továrnička může vytvářet/vracet jedinou instanci.
Teď ji ještě uprav tak, aby byla konfigurovatelná a dostupná ze
systémového kontextu (např. přes
Environment::getContext()->getService('authenticator')
).
Tady jsem navrhoval jen to, že by se mi líbilo, kdyby
$defaultServices
bylo nahrazeno nějakýmnette.neon
přímo ve frameworku, aby pro tu samou věc neexistovala dvojí syntaxe.
Neumím si představit, jak by se zapisoval kód některých továrniček v neonu.
Já vůbec neříkám, že je Nette na odpis, ale že ten mnou prezentovaný přístup mi přijde správný a univerzální, tak bych ho rád sjednotil napříč celým frameworkem. Proč Template nedostává celý kontejner, ale jen Translator setterem? Proč jiné objekty dostávají celý kontejner?
Chování je jednotné.
- Filip Procházka
- Moderator | 4668
Davídku, udělej nám zase kousek, ať se nám to pohne :) Já úplně slintám na to, až tam budou tagy, vyměnitelnost Configuratoru a ContainerBuilder/Compiler :) Jestli se ti nechce, tak to spatláme, že :)
Každý by mohl poslat pár pull requestů s úpravou toho co se mu nelíbí, s přiloženým RFC a pak na PS půjčíme Davidovi MacBook a všichni budou spokojení :)
Editoval HosipLan (16. 5. 2011 8:48)
- David Grudl
- Nette Core | 8218
HosipLan napsal(a):
Davídku, udělej nám zase kousek, ať se nám to pohne :) Já úplně slintám na to, až tam budou tagy, vyměnitelnost Configuratoru
Vyměnitelnost Configuratoru by byla úplná revoluce. Ale nedám ji tam.
- Filip Procházka
- Moderator | 4668
Já tak rád slovíčkařím :) https://github.com/…tte/pull/242 Něco takového jsem myslel… Ale tohle je asi ta poslední věc a dá se bez toho žít
- Patrik Votoček
- Člen | 2221
:-DDDD ale líbilo by se mě kdyby byl konfigurátor reprezentován interfacem a né třídou
- David Grudl
- Nette Core | 8218
Zajímalo by mě, k čemu konkrétně v praxi potřebuješ použít vlastní Configurator. (kvůli zamýšlené úpravě)
- Patrik Votoček
- Člen | 2221
Třeba kvůli tomu že si chci přidat kompilátory. Pak taky snadno nahradit Nette\DI\Context za jinou chytřejší třídu.
Hlavní je přesunout továrničky z konfigurátoru jinam a
z konfigurátoru udělat Nette\DI\ContainerBuilder
. Nebo naopak
ponechat konfigurátor a Nette\DI\ContainerBuilder
přidat.
Editoval Patrik Votoček (16. 5. 2011 15:47)
- David Grudl
- Nette Core | 8218
Jelikož Configurator má prakticky za jediný úkol vyrobit systémový
context (tj Environment::getContext()
, přesouvat tento úkol jinam
nedává moc smysl.
Místo výměny konfigurátorů se mi zdá pružnější umožňit použití
vlastního kontextu (tj. přidat Environment::setContext()
).
ContainerBuilder je obecná třída pro práci nad obecným kontejnerem. Zřejmě ji do frameworku přidám, aby uměla načíst třídy z pole a případně vygenerovat PHP cache.
- Patrik Votoček
- Člen | 2221
David Grudl napsal(a):
Jelikož Configurator má prakticky za jediný úkol vyrobit systémový context (tj
Environment::getContext()
, přesouvat tento úkol jinam nedává moc smysl.
Ano to chápu ale já třeba v Nella Frameworku používám / chci používat trochu jiné výchozí nastavení systémového kontextu. A dědit od třídy která obsahuje továrničky (tj. zbytečný balast který nepotřebuju) mě připadá zbytečné a né zrovna správné.
Místo výměny konfigurátorů se mi zdá pružnější umožňit použití vlastního kontextu (tj. přidat
Environment::getContext()
).
Environment::getContext()
už je né? spíš jsi chtěl říct
Environment::setContext()
nebo se mýlím?
ContainerBuilder je obecná třída pro práci nad obecným kontejnerem. Zřejmě ji do frameworku přidám, aby uměla načíst třídy z pole a případně vygenerovat PHP cache.
jde mě o něco jako jsem popsal tady: https://forum.nette.org/…cy-injection#…
v Symfony 2 to myslím není IConfigurator
ale
ICompiler
.
- David Grudl
- Nette Core | 8218
Jasně, setContext(). Prostě nemusíš vůbec Configurator dědit. Obecně vzato, vůbec nemusíš používat ani třídu Environment, ani třídu Configurator a rovnou pracovat s vlastním DI kontejnerem.
- Patrik Votoček
- Člen | 2221
Já ale nechci pracovat s vlastním kontejnerem nýbrž nahradit systémový za svůj vlastní.
- Tharos
- Člen | 1030
Trochu odbočím /respektive vrátím se k jádru tématu ;)/. Všiml jsem
si, že kontextu předávanému Application
se z „globálního“
kontextu předává služeb vcelku
poskrovnu. Pokud jich chci předat více /například
cacheStorage
, abych nemusel pro přístup ke cache všude volat
Environment::getCache()
a měl vše hezky cool DI a mohl se
v hospodě chvástat, jak nikde v aplikaci nepoužívám třídu Environment
;)/, plus bych si rád do contextu Appliction
předal
i například službičku config
(mám v neonu i řadu
nastavení, se kterými pracují komponenty a podobně), tak mi nezbývá, než
si nadefinovat úplně vlastní
createApplication
:„https://api.nette.org/2.0/source-common.Configurator.php.html#299“
(a skoro shodnou s původní, jen doplněnou o předání těch několika
dalších služeb z globálního kontextu) a používat tu. Chápu to
správně?
A zároveň, jestli to tedy dobře chápu, je toto v podstatě nejsnazší způsob, jak v duchu DI používat něco z config.neon (jak se tu na fóru teď někdo někde ptal). Je to významný krok kupředu, dobrá práce!
Jo a upozorňuji, že svůj dotaz nezamýšlím jako nějakou skrytou kritiku nebo tak :), nový koncept nemám problém přijmout za svůj a chápu jeho výhody.
- Patrik Votoček
- Člen | 2221
Tharos napsal(a):
Všiml jsem si, že kontextu předávanému
Application
se z „globálního“ kontextu předává služeb vcelku poskrovnu. … (mám v neonu i řadu nastavení, se kterými pracují komponenty a podobně)
Pozor ale do Presenteru (potažmo komponent) je předáván celý systémový kontainer. Tenhle "skromný je jenom v Application :-)
Editoval Patrik Votoček (16. 5. 2011 23:55)
- David Grudl
- Nette Core | 8218
Pokud zrovna nevytváříš potomka třídy Application, tak žádné další služby by sis do něj přidávat neměl. On je nepotřebuje. Je to totéž, jako zavolat funkci s více parametry, než očekává / přijímá. Nemá to žádný efekt.
Služby si tedy předávej přímo tam, kde je budeš potřebovat. Do presenteru například pomocí PresenterFactory.
- Patrik Votoček
- Člen | 2221
navrhuji public Application::getContext()
na
protected
nebo úplně zrušit
Editoval Patrik Votoček (17. 5. 2011 0:39)
- Filip Procházka
- Moderator | 4668
Jó to dává smysl! :) Protože to tady dost lidí mate…
//edit: spíš protected, co kdybych chtěl dědit…
Editoval HosipLan (17. 5. 2011 1:16)
- Patrik Votoček
- Člen | 2221
Ještě mě tak napadá je v plánu aby Environment::
- setVariable
- getVariable
- getVariables
- expand
- getService
- getHttpRequest
- getHttpResponse
- getHttpContext
- getApplication (tady si nejsem úplně jistý)
- getUser
- getRobotLoader
- getCache
- getSession
- getConfig
vyhazovaly deprecated nebo byly rovnou odstraněny?
- David Grudl
- Nette Core | 8218
V plánu není. Environment je statická obálka nad systémovým kontejnerem. Zbytečné jsou teď jen getVariable a getConfig, které dělají to stejné.
- arron
- Člen | 464
Tharos napsal(a):
Díky moc vám oběma za nakopnutí, konečně mi to všechno secvaklo a zapadlo hezky dohromady.
A zvažte lock tohoto vlákna, protože když už to secvaklo i mně, tak to už asi opravdu musely být všechny smysluplné otázky položeny a zodpovězeny ;).
Jeste ne, protoze me to porad jeste tak uplne do sebe nezapadlo :-) Ale predpokladam, ze po vydani finalni verze k tomu bude podrobna dokumentace (?) :-)
- Filip Procházka
- Moderator | 4668
Všecko co potřebuješ vědět je přece v úvodním příspěvku, nebo v odkazovaných vláknech. Pokud to nechápeš tak si asi ještě nedošel do stádia „týjo! to DI je cool! to chci teď používat!“ a můžeš s klidem dál pracovat jako do teď :)
- Filip Procházka
- Moderator | 4668
Na DI není vůbec nic magického, nebo složitého. Je to jenom pár
jednoduchých principů, které když dodržíš, tak budeš mít čitelnější
a lépe testovatelný kód. Mno a jako bonus tu je DI\Container
,
který pomáhá služby sestavovat a nastavovat. That's all folks :)