Dependency injection si razí cestu do 2

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
David Grudl
Nette Core | 8227
+
0
-

Jedna z klíčových věcí v jedničce bude návrat ke kořenům, k dependency injection. Používání této techniky z frameworku spíš časem vyprchalo a dnes programátor spíš šáhne po statické třídě Environment, než aby využil kontejneru ServiceLocator. Chtěl bych to změnit.

Jednak samotný kód frameworku nebude nikde třídu Environment využívat a striktně přejde na objekt service locatoru. Samotný lokátor chci zjednodušit a lépe zasadit do frameworku. Docela uvažuju nad tím, že bych ho přejmenovat, přeci jenom dlouhý název je taky odrazující ;-) (koneckonců právě proto se Environment jmenuje Environment).

Nechci použít v DI užívaný název container, protože ve frameworku už existují kontejnery s jiným významem. Tuším se používá i název provider, ten je zatím „volný“. Podobně je na tom název context. Čemu byste dali přednost?

(pokud nevíte, o čem je řeč, brzy vysvětlím)

paranoiq
Člen | 392
+
0
-

jupí! :]

vyšel bych ze SeviceProvider a možná to zkrátil Provider. Context je takové divné – významem mi to připomíná Environment (kontext aplikace – prostředí aplikace)

Patrik Votoček
Člen | 2221
+
0
-

WOW cool. No mě se to zkracování moc nelíbí. ServiceLocator mě nepřipadá dlouhé. Ale kdybych se musel rozhodnout asi bych volil provider.

Nilp
Člen | 65
+
0
-

Osobně mi Provider/ServiceProvider připadá určitě lepší. Jakou variantu zvolit IMO záleží na tom, jak to bude nakonec vypadat (používat název service pro všechny DI-managed třídy mi nepřijde dobré).

Vyki
Člen | 388
+
0
-

Provider se bude psát dobře

redhead
Člen | 1313
+
0
-

Provider +1

Ondřej Mirtes
Člen | 1536
+
0
-

Context je moc pěkný název a přesně vyjadřuje jeho podstatu :) Taky jsme si s ním v Mediu dali práci :o)

redhead
Člen | 1313
+
0
-

Context mi spíše připomíná z Javy to, co je v Nette HttpRequest.

rarous
Člen | 59
+
0
-

Kernel

Dragon Jake
Člen | 20
+
0
-

a co takhle Manager?

rarous
Člen | 59
+
0
-

Manager je ukázkový případ zoufalosti, kdy netušíš, jak to pojmenovat. Vždycky existuje výstižnější název. ;)

Cifro
Člen | 245
+
0
-

Mňa napadol ServiceContainer ale to je ešte dhlšie ako ServiceLocator :D

Ale inak sa stotožňujem s týmto:

vrtak-cz napsal(a):

ServiceLocator mě nepřipadá dlouhé. Ale kdybych se musel rozhodnout asi bych volil provider.

arron
Člen | 464
+
0
-

Myslim, ze ServiceLocator danou vec vystihuje velmi pekne a nevidim duvod (v dobe kvalitnich IDE) to nejak zkracovat. Navic bych rekl, ze zbytecne vymyslime horsi pojmenovani pro hezky pojmenovanou vec jenom proto, abychom to pojmenovali jinak…

Dragon Jake
Člen | 20
+
0
-

rarous napsal(a):

Manager je ukázkový případ zoufalosti, kdy netušíš, jak to pojmenovat. Vždycky existuje výstižnější název. ;)

aneb jak je slovo manage v angličtině univerzální, pravda :) provider se mi líbí dostatečně.

rarous
Člen | 59
+
0
-

BTW ServiceLocator není DI. Dokonce je to DI antipattern. Něco takového zavádět není moc dobrý nápad. V DI se používají názvy Container, Kernel nebo ObjectFactory. Pokud nechcete použít Container, který je IMO nejsprávnější, použijte Kernel. Jiné názvosloví bych rozhodně nezaváděl.

David Grudl
Nette Core | 8227
+
0
-

Vyjádřil jsem se docela nešťastně, nechci přejmenovávat Service Locator kvůli délce, to je podřadný důvod se smajlíkem. Spíš si nejsem jist jeho vhodností. Jednak je to zaběhlý pojem pro návrhový vzor, o jehož implementaci se nesnažím a locator v Nette nemusí nijak korespondovat se Service Locatorem jinde. Provider nebo Context asi blíže odpovídají záměru, který hned vysvětlím. U termínu Provider mi vadí jen to, že automaticky následuje otázka „poskytovatel čeho?“. Context si pamatuju z Mojavi blahé paměti a připomněl mi ho Ondra s Honzou Tichým.

Popíšu současný stav: Pokud chci v bootstrapu použít Application, kdekoliv přistoupit k HttpRequest nebo k úložišti cache, nevytvářím si instanci třídy sám, ale požádám o ni ServiceLocator. Což je vlastně chytřejší registr objektů, který umí kromě samotných objektů registrovat i callbacky na továrničky, viz dále. Přičemž Nette vytváří i defaultní nebo chcete-li iniciální lokátor (třída Configurator), který je dostupný skrze třídu Environment.

Co je vlastně smyslem locatoru? Řeší úskalí dependency injection. Totiž, pokud chci v nějaké komponentě pracovat s aktuálně přihlášeným uživatelem, mám dvě možnosti, jak to udělat:

  1. předám ji přes konstruktor nebo setter objekt User (DI)
  2. nepředám ji nic a komponenta si „magicky“ šáhne na Environment::getUser() (to nechceme)

S bodem 1) souvisí jeden vážný a jeden méně vážný problém. Abychom mohli objekt User předat, musí existovat jeho instance. Ale co v případě, že vytvoření objektu User je drahé a komponenta ho potřebuje jen občas? Hodil by se nám nějaký lazy přístup. Druhý problém je ten, že kdejaká komponenta používá celou řadu objektů-služeb a počet setterů nebo parametrů v konstruktoru by neúměrně rostl.

Řešením je právě zmíněný service locator – předám jeden objekt a ten nese balík všech dalších objektů. Navíc má schopnost registrovat i továrničky, takže při prvním požádání o např. HttpRequest se zavolá továrnička a ta objekt sestaví a locator jej nadále vrací. Samotný volající objekt přitom nepozná rozdíl. A oba uvedené problémy se tím vyřeší.

Takhle funguje Nette snad odjakživa a zdá se mi to vcelku OK. Pojďme na to, co OK není.

Jednak dnes se využívá prakticky jen ten iniciální lokátor a to navíc přes globální volání. Namísto toho, aby se do jednotlivých komponent propagoval vlastní lokátor. Chybu vidím v tom, že používání vlastního lokátoru bylo vždycky méně cool, než napsat Environment::getUser(). I přes ten krkolomný název ;-) A dále jsou tu úzká místa Nette, kupříkladu třídě Application nastavit vlastní lokátor jde blbě, k tomu určená $defaultServices není dostatečná cesta.

Řešením je samozřejmě přidat do Application & spol. metodu setServiceLocator (nebo využít run(), jak navrhovali Mediáci), ale také předělat API samotného locatoru. Přeci jen $this->locator->getService('Nette\Web\IUser') těžko trumfne Environment::getUser().

Filip Procházka
Moderator | 4668
+
0
-

co takhle naučit ServiceLocator aliasy jako to umí Environment

$locator->setAlias('user', "Nette\\Web\\IUser");

// potom kdekoliv v komponentách/application... apod
$this->locator->getUser();

Editoval HosipLan (16. 9. 2010 14:54)

Patrik Votoček
Člen | 2221
+
0
-

Nevím proč mě připadá že na konci chybí odstavec:

Proto v 1.0 bude lokátor řešen $this->foo … Jen přijít na ten vhodný název pro foo.

To výše je jenom příklad.

Nebo jsem úplně mimo?

Blizzy
Člen | 149
+
0
-

Ve frameworku se vyskytuje pouze ComponentContainer, podle mě by se ten pozměněný ServiceLocator měl jmenovat ServiceContainer. Zmatení by to snad nezpůsobilo, komponenty a služby jsou diametrálně odlišné věci.

Název ServiceLocator ve mně evokuje, že jde o jeden objekt, singleton. Název ServiceContainer mi připadá spíše vhodný pokud má být objekt předáván, zdá se mi to lepší název, pokud těchto objektů bude více.

Editoval Blizzy (16. 9. 2010 21:43)

jantichy
Člen | 24
+
0
-

Za sebe samozřejme hlasuju pro Context, protože to vystihuje podstatu a smysl užití mnohem lépe, než Provider.

Provider, stejně jako ServiceLocator tak trošku zavání tím, že je to nějaký „globální“ autoritativní poskytovatel napříč celou aplikací. Jakože když chci identitu uživatele nebo doctrine entityManager, tak že je to stejná hodnota napříč celou aplikací a dostanu z toho to samé, ať se zeptám, kde se zeptám.

Naproti tomu Context vyjadřuje celou podstatu věci, totiž že na každém jednom dílčím místě aplikace to může obsahovat úplně jiné věci, podle toho, jak se zrovna volající nadřazený kód rozhodl a jak to potřebuje.

Takže moje +1 pro Context.

paranoiq
Člen | 392
+
0
-

vzhledem k tomu, jak to David dovysvětlil, tak i já jsem spíš pro context

Context +1

pekelnik
Člen | 462
+
0
-

vzhledem k tomu, jak to Honza dovysvětlil, tak i já jsem spíš pro context :)

Context +1

VasekPurchart
Člen | 20
+
0
-

context +1

redhead
Člen | 1313
+
0
-

Po vysvětlení taky context +1

David Grudl
Nette Core | 8227
+
0
-

Když to tak čtu, jsem taky pro context :-)

rarous
Člen | 59
+
0
-

A čeho je to kontext, smím-li se zeptat? :)

jantichy
Člen | 24
+
0
-

A čeho je to kontext, smím-li se zeptat? :)

Chápu to jako celkový kontext/konfiguraci/kombinaci všech injectovaných záležitostí, které daný objekt, do kterého se to injectuje, využívá. Je to kontext, v rámci kterého je daný objekt vytvářený/volaný/prováděný.

Že jsem kverulant, ono by se to taky mohlo jmenovat IOC ;P

Roman Ožana
Člen | 52
+
0
-

David Grudl napsal(a):

Když to tak čtu, jsem taky pro context :-)

Context :)

kravčo
Člen | 721
+
0
-

„Kontext“ vysvetľuje akademický slovník cudzích slov pod 2. ako súvislosť vôbec, súbor súvislostí v nejakom dianí; napr. kontext doby.

A v podstate súvislosť, či súbor súvislostí to je i v tomto prípade. K nejakému objektu pripojím jeho kontext – súbor súvisiacich objektov (NUser, NHttpRequest, …), ktoré môže transparentne využívať. Kontext mi zase umožní využívať DI, takisto môže zabezpečiť lazy loading a ďalšie fičúry…

rarous
Člen | 59
+
0
-

Jenže souvislost nemůže být obecná :) Souvislost/kontext je vždy k něčemu vztažen. Není to dobrý název. :) když už se vám nelíbí zaběhnutý kernel, IoC je opravdu krátké :)

David Grudl
Nette Core | 8227
+
0
-

Kernel se mi vážně nelíbí. Když jsi to tu prve psal, považoval jsem to za fórek.

Z pohledu .NET vývojáře můžeš mít pocit, že tu vynalézáme kolo a do jisté míry budeš mít pravdu. Ale tyto věci nejsou u PHP frameworků zaběhlé, existují tu trošku jiné potřeby a nemusíme ani opakovat chyby jiných implementaci (tj. uděláme si vlastní). Název kernel bych třeba za chybu názvosloví považoval.

v6ak
Člen | 206
+
0
-

IoC je popis stavu („je to naopak“) a je IMHO nevhodný pro název třídy. Celkově tento název nemám rád, protože on vlastně za normální označuje stav, který já považuji za nenormální. Ostatně, do zavedení DI jsem ve svém kódu viděl něco ošklivého, nenormálního, co by asi mělo být řešeno nějak jinak…

Kontext – proč ne? Něco provedu v kontextu aktuálního HTTP požadavku s aktuálním uživatelem. Nebo provedu něco jako $application->run() v kontextu jiného HTTP požadavku (nápad převzat ze článku Singleton Sofie S. ). Podstatné je, že tu není něco jako „globální kontext“ – onen vnitřní požadavek nemůže zjistit, jestli je vnitřní.

arron
Člen | 464
+
0
-

Mno…jsem zvedavy, kam se to jeste dal posune, ale dle meho nazoru mame z vse rikajiciho ServiceLocatoru nic nerikajici Context. Ale budiz…

rarous
Člen | 59
+
0
-

No, když už tedy máte Texy, dibi, nette, proč nevymyslet krásný název i pro IoC kontejner? :)

Tvoje implementace (užívající lazy initialization pomocí factories) opravdu neni ServiceLocator, ale ani žádný Context.

Když to vezmu z pohledu implementací IoC v .NET, tak se používá WindsorContainer, který vnitřně použíná Kernel (MicroKernel), pak je tu Ninject, který má Kernel, StructureMap používá ObjectsFactory, Autofac používá Container, Unity nejspíš taky užívá termínu Container a Spring.net používá ApplicationContext.

Myslím si, že jednotný slovník s jinými platformami neni na škodu (i přes výhodu možnosti zavést si vlastní názvosloví).

Blizzy
Člen | 149
+
0
-

Context je dobrý název, ale pod instancí třídy Context si nepředstavím objekt, kam registruju a nastavuju jiné třídy, objekty a továrničky na objekty. Stále si stojím za zmíněným ServiceContainerem. Tento název používá i Symfony implementace DI containeru a je velmi podobný i se zmiňovanými .NET implementacemi. Nicméně kontext už má hodně hlasů, a můj názor na to asi nic nezmění :).

Patrik Votoček
Člen | 2221
+
0
-

to zní asi nejlíp z toho co tu zaznělo…

grey
Člen | 94
+
0
-

já se stavím za ServiceContainer, přijde mi to jako nejjasnější název, a dlouhý mi nepřijde.

Filip Procházka
Moderator | 4668
+
0
-

Přečtěte si co napsal David a potom co napsal Honza Tichý, přečtěte si to ještě jednou a pokud furt nebude jasné jaký má mít ta třída význam tak se běžte podívat na github kde už je to předělané a zvykejte si :)

https://github.com/…10e3793d0161

Lopata
Člen | 139
+
0
-

Nevím, jestli nejsem příliš zhýčkaný programátor nebo jestli vy programujete v notepadu, ale IDE vám stejně napovídá, takže tu délku bych zas tak horkou neviděl. Myslím, že i kdyby se to nakonec jmenovalo Nejneobhospodarovavatelnejsimi, každý stejně napíše „Nej“, dolů enter…

Editoval Lopata (16. 9. 2010 22:55)

Ondřej Mirtes
Člen | 1536
+
0
-

Když už jsme u těch DI změn, tak se přimlouvám za dvoukrokový bootstrap, kdy si mezi prvním (matchování rout) a druhým (spuštění presenteru) budu moci do presenteru vstříknout context (a zároveň v něm budu moci využít informace z proběhnutého routování, typicky locale). Nyní je potřeba to dělat přes Environment, což je nešikovné.

Taky předpokládám, že $this->context bude přistupný i v controlech, předávaný nejspíš jako parametr konstruktoru (záměrně neříkám kolikátý) nebo addComponent. On bude muset být přístupný třeba i ve formulářích, které pracují s HttpRequest, že :)

Určitě bych do contextu přidal i config.

A ještě drobnost k API – abych v aktuálním objektu contextu vyměnil jednu implementaci nějakého rozhraní za jinou (kvůli čemuž to vstřikování vlastně dělám), musel bych se zeptat na hasService, případně provést removeService a nakonec addService, což je nepohodlné. Šla by zavést metoda setService, která by se nekoukala, zdali v objektu už ta daná implementace je, a prostě ji bez ohledu na ni přebyla?

Ani
Člen | 226
+
0
-

Popravdě použití context ve vzahu k tomu pořádně nechápu… Myslim, že z toho bude spousta lidí zmatená. Ten ServiceContainer mi přijde asi nejvíc srozumitelný.

David Grudl
Nette Core | 8227
+
0
-

HosipLan napsal(a):

Přečtěte si co napsal David a potom co napsal Honza Tichý, přečtěte si to ještě jednou a pokud furt nebude jasné jaký má mít ta třída význam tak se běžte podívat na github kde už je to předělané a zvykejte si :)

Ale kurňa, to mi tam uletělo z vývojové větvě…

v6ak
Člen | 206
+
0
-

No, parametr Controlu: nebylo-li by určeno jinak, šlo by to přebírat z parent Controlu. Takže klidně jako třetí parametr.

Honza Marek
Člen | 1664
+
0
-

Ne, že bych měl někomu ve zvyku kecat do názvosloví. Na to se necítím být dostatečně vzdělán ve stopadesáti programovacích jazycích a frameworcích. Ale co to sakra je context?

Filip Procházka
Moderator | 4668
+
0
-

Honza Marek:
imho to bude třída, která bude poskytovat konrétnímu objektu služby, které s ním nějak souvisí

Jak máš teď $defaultServices v Application, tak to s tím bude pravděpodobně nějak souviset.
Dalším třídám se to bude asi přes konstruktor předávat, například třídě Control nebo Presenter.
Čímž docílíš například (a nevím jestli je to nejvhodnější příklad) toho, že nebudeš moct „systémově“ šahat z komponenty do Routeru, protože bude přes Context přístupný pouze v Application, takže pak nepůjde $presenter->context->getRouter(); ale $presenter->application->context->getRouter(); (vím že to jde teď $application->getRouter()). Bude zkrátka přístupné pouze to co s třídou souvisí.

Chápu to správně? :)

PS: Router je blbý příklad, ale lepší se mi nechce vymýšlet :P

na1k
Člen | 288
+
0
-

HosipLan, a kde nadefinuju, které služby souvisí s kterým objektem?

Asi mi pořád uniká smysl této změny. Proč nestačí zrušit Environment a jeho „služby“ přesunout do ServiceLocatoru, které by byly přístupné např. v Control přes $this->locator->getService('Nette\Web\User') ?

(Vlákno jsem četl asi třikrát…)

David Grudl
Nette Core | 8227
+
0
-

ad Container vs. Context: vidím tady určitý implementační rozdíl, zatímco pod pojmem Container se v DI rozumí ten chytrý objekt co ví, jak poskytované objekty sestavit, Context je spíš jen malinko chytřejší přenašeč. Ironií je, že o to více mi připadá zaběhlý pojem Container v DI jako špatný.

Filip Procházka
Moderator | 4668
+
0
-

na1k: nejsi sám koho to zajímá :) pár teorií jak to udělat mám,
ale nepochybuju že David vymyslí něco lepšího, takže se nebudu ani snažit to rozepisovat :)

kravčo
Člen | 721
+
0
-

HosipLan napsal(a):

… ale nepochybuju že David vymyslí něco lepšího, takže se nebudu ani snažit to rozepisovat :)

a mohol si sa veľa naučiť…

David Grudl
Nette Core | 8227
+
0
-

na1k napsal(a):

Proč nestačí zrušit Environment a jeho „služby“ přesunout do ServiceLocatoru, které by byly přístupné např. v Control přes $this->locator->getService('Nette\Web\User') ?

Nejen, že to stačí, ono už to tam dávno je. Ale nikdo to nepoužívá. Proto chci věc zjednodušit, upravit, osvěžit, navonět a hlavně dotáhnout nedostatky do konce.

HosipLan napsal(a):

ale nepochybuju že David vymyslí něco lepšího, takže se nebudu ani snažit to rozepisovat :)

Ale jen se rozepisuj, vždyť tohle je takový brainstorming a patent na rozum skutečně nemám.