Dependency Injection Container: TODO

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

Nemáte někdo chuť implementovat do DIC

  • podpora anotací @inject u veřejných proměnných? (hotovo)
  • automatické vytváření accessorů, podobně jako nyní továren? (hotovo)
Filip Procházka
Moderator | 4668
+
0
-

Nechtěl jsem zakládat flame, ale protože je to v diskuzi a né feature request…

V presenterech je potřeba být lazy co nejvíc, na tom se určitě shodneme. Implementace je tedy k diskuzi, jedna trošku prasácká cesta už je a zdá se být docela oblíbená. Další zajímavé se vynořují.

Když ale začnou lidé cpát tyhle zbytečnosti do modelu, začne vznikat cosi odporného a na každém druhém řádku bude něco ve stylu

$this->db->get()->query(...);

protože co kdyby se databáze nepoužila…

Navíc tím budeš nepřímo navádět lidi, aby vytvářeli god objekty, protože proč ne – celé je to lazy :) Ospravedlňuješ tím prasení.

Teď accessory nejsou a všichni se snažíme vést nettisty k psaní malých tříd s minimem závislostí a odpovědností. Když něco chceš v třídě, předej si to přes konstruktor, má třída hodně závislostí? Asi toho dělá víc než by měla.

Další problém je, že když bude v Nette @inject by default, tak se tahle metrika vypaří, protože napsat 10 závislostí pod sebe schovaných v třídě pere do očí daleko méně, než když si je musím explicitně předat přes konstruktor.

No a když už budeme mít inject na veřejné proměnné, proč ho rovnou nepovolit i na protected a private :) A to se blbě vysvětluje…

Prosím Davide, neustupuj – ty jsi strážce kvality proslulého Nette Frameworku a tohle není dobrá cesta :)


Mám nápad – vymyslíme jak to implementovat jako rozšíření a upravíme Nette\DI (a Nette\Application) tak, aby tyhle rozšíření hackovaly Nette co nejméně. Budeme mít rozšířitelnější Nette\DI a nebudeme by default navádět k tomuhle java-like prznění objektů :)

WIN – WIN


Ještě doplním jednoduché měření, co prováděl Majkl578 – mluví ve prospěch malých objektů s primitivním konstruktorem. Accessory jsou zbytečné. Vždy to jde řešit lépe a bez nich.

Editoval Filip Procházka (12. 3. 2013 20:09)

hrach
Člen | 1834
+
0
-

DG: +1 :)

David Grudl
Nette Core | 8099
+
0
-

Filip Procházka napsal(a):

Když ale začnou lidé cpát tyhle zbytečnosti do modelu, začne vznikat cosi odporného a na každém druhém řádku bude něco ve stylu

$this->db->get()->query(...);

protože co kdyby se databáze nepoužila…

Toho se rozhodně neobávám. Vždy je jednodušší napsat /** @inject @var Nette\Database\Connection */ a pak přímo pracovat s databází, než vytvářet interface accessoru a pak všude používat $this->db->get().

Navíc tím budeš nepřímo navádět lidi, aby vytvářeli god objekty, protože proč ne – celé je to lazy :) Ospravedlňuješ tím prasení.

Nechápu… Co je lazy? A jak souvisí lazy s god objekty?

Teď accessory nejsou a všichni se snažíme vést nettisty k psaní malých tříd s minimem závislostí a odpovědností. Když něco chceš v třídě, předej si to přes konstruktor, má třída hodně závislostí? Asi toho dělá víc než by měla.

Na tomhle anotace @inject, accessory ani továrny absolutně nic nemění. Opět jde jen o objekt, který se předává.

Další problém je, že když bude v Nette @inject by default, tak se tahle metrika vypaří, protože napsat 10 závislostí pod sebe schovaných v třídě pere do očí daleko méně, než když si je musím explicitně předat přes konstruktor.

Ty, které tahle metrika nezajímá, si stejně žádné závislosti nepředávají a používají statiku. Ty, které zajímá, nepotřebují, aby je to pralo do očí. A psát zbytečný/rutinní kód navíc otravuje oba tábory.

No a když už budeme mít inject na veřejné proměnné, proč ho rovnou nepovolit i na protected a private :) A to se blbě vysvětluje…

To je stejný případ jako protected metoda inject().

Prosím Davide, neustupuj – ty jsi strážce kvality proslulého Nette Frameworku a tohle není dobrá cesta :)

Jsem přesvědčen, že je ;)

Šaman
Člen | 2633
+
0
-

@Filip Procházka:
V psaní ošklivého kódu mi Nette nemůže zabránit, když jsem lama a trochu se snažím. Koneckonců ani nemusím používat model a jet na VC architekturu.

A injecty v modelu používám už teď, jen je musím plnit v configu. Jsou to prostě jinak pojmenované settery. Mnohdy potřebuji předávat modely do servisní vrstvy lazy – nevím se kterým repozitářem budu pracovat, protože servisní vrstva mi obaluje velkou část modelu. A že to vypadá, že ta moje servisní vrstva dělá hodně věcí? Jen to tak vypadá – většinou deleguje požadavek na některý repository, ale třeba pokud chci třeba založit novou s novým autorem, už se bez ní neobejdu. (Pokud bych to nenechával na servisní vrstvě, tak mi bude muset autora založit BookRepository a opět potřebuji lazy přístup k AuthorRepository. Přece nebudu vytvářet AuthorRepository (do konstruktoru), pokud si budu chtít jen přečíst seznam knih v kategorii..)

spenat28
Člen | 9
+
0
-

DG: +1, i luv property injection

Honza Marek
Člen | 1664
+
0
-

Šaman napsal(a):

Přece nebudu vytvářet AuthorRepository (do konstruktoru), pokud si budu chtít jen přečíst seznam knih v kategorii..)

Nechápu, kde se bere to přesvědčení, že vytvoření nějakého lazy bazmeku (což je obyčejný objekt) je levnější než vytvoření běžné service (což je obyčejný objekt).

Filip Procházka
Moderator | 4668
+
0
-

David Grudl napsal(a):

Navíc tím budeš nepřímo navádět lidi, aby vytvářeli god objekty, protože proč ne – celé je to lazy :) Ospravedlňuješ tím prasení.

Nechápu… Co je lazy? A jak souvisí lazy s god objekty?

Myslel jsem tím accessory. Vsadím se o oběd v indické, že do roka a do dne, od momentu kdy bude ve stable Nette podpora accessorů, se nám tady na fóru objeví člověk, který bude mít na naprostou většinu služeb accessory a bude je používat na vše :) A ani se za to nebude stydět.

Jsem skálopevně přesvědčen, že generované accessory jsou zbytečné a pouze uživateli ospravedlňují god objekty.

Na tomhle anotace @inject, accessory ani továrny absolutně nic nemění. Opět jde jen o objekt, který se předává.

Tak jak jsi to implementoval teď, tak je to ok – protože je to čisté DI, předáváš do public proměnné.

Ovšem stačilo pár hodin a moje slova se plní

No a když už budeme mít inject na veřejné proměnné, proč ho rovnou nepovolit i na protected a private :) A to se blbě vysvětluje…


Honza Marek napsal(a):

Nechápu, kde se bere to přesvědčení, že vytvoření nějakého lazy bazmeku (což je obyčejný objekt) je levnější než vytvoření běžné service (což je obyčejný objekt).

+1

Editoval Filip Procházka (13. 3. 2013 10:02)

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Ach ty pojmy…

Šaman napsal(a):

  • předávat modely do servisní vrstvy
  • servisní vrstva mi obaluje velkou část modelu

Cožpak servisní vrstva není součástí modelu (toho pod M v MVC)? Všechno jsou to jen třídy nějak implementující business logiku aplikace, ne? Ano, dá občas práci navrhnout to dobře, ale Nette umožňuje vytvářet krásnou architekturu s tak velkou dávkou pomoci, že snad není rozumné výmluvy :). Proč prostě neudělat hezkou třídu na vytvoření knihy s novým autorem, a předat jí co potřebuje? Svázání jakýmsi rozdělením tříd do systému neslučitelných kast z toho úplně čiší.

David Grudl
Nette Core | 8099
+
0
-

Honza Marek napsal(a):

Nechápu, kde se bere to přesvědčení, že vytvoření nějakého lazy bazmeku (což je obyčejný objekt) je levnější než vytvoření běžné service (což je obyčejný objekt).

A skutečně: new stdClass (obyčejný objekt) je levnější, než new PDO (obyčejný objekt).

Pochopitelně člověk se musí řídit rozumem, jinak režie lazy obálek výkon sníží.

Tharos
Člen | 1030
+
0
-

Ale PDO, které se okamžitě připojuje k databázi, není vůbec jen tak nějaký obyčejný objekt :).

Nepřipadá vám (zastáncům „accessorů“), že těch objektů, pro které je vytvoření nějaké proxy třídy skutečně přínosné, je v typické aplikaci úplné minimum? Pokud ano (a já si to myslím), tak těch pár jednotek pomocných tříd přece nemá smysl generovat. To si každý napíše snadno sám ručně.

Skoro bych se vsadil, že zavedení automatického generování něčeho podobného povede k nadužívání a přinese to pouze negativní dopady (pomalejší a méně přehledné aplikace).

David Grudl
Nette Core | 8099
+
0
-

Tharos napsal(a):

Nepřipadá vám (zastáncům „accessorů“), že těch objektů, pro které je vytvoření nějaké proxy třídy skutečně přínosné, je v typické aplikaci úplné minimum?

Pochopitelně. Tvrdí někdo opak?

Pokud ano (a já si to myslím), tak těch pár jednotek pomocných tříd přece nemá smysl generovat. To si každý napíše snadno sám ručně.

A pravděpodobně blbě, tj. napíše pomocnou třídu, ale už nenapíše interface, jehož je tato třída implementací. Bez interface vytvoří závislost na DI kontaineru.

Pokud mi napíše interface, rád mu tu implementaci vygeneruju: řešení pak bude čisté.

Skoro bych se vsadil, že zavedení automatického generování něčeho podobného povede k nadužívání a přinese to pouze negativní dopady (pomalejší a méně přehledné aplikace).

Jasně, už vidím, jak všichni píšou desítky rozhraní accessorů pro každou službu a poté k nim o něco složitěji přistupují.

Filip Procházka
Moderator | 4668
+
0
-

David Grudl napsal(a):

Jasně, už vidím, jak všichni píšou desítky rozhraní accessorů pro každou službu a poté k nim o něco složitěji přistupují.

No hele, upřímně … tebe by to překvapilo?

Můžu ti upřímně říct, že vím minimálně o jednom projektu, který to už teď dělá na základě tvého článku – píšou si accessory na opravdu hodně věcí. Tušíš co by to s takovým projektem udělalo, kdyby jim to Nette zvládlo generovat? Nechci ani domýšlet, takhle brzo po obědě :)

A proč by to vlastně někdo dělal? Protože má starou aplikaci, která je zbastlená a pomalá. V Nette se objeví feature, že služby se budou vytvářet lazy… Kdysi někde četl, že se přece používá teď to DI, tak si předává třídy, ale má jich tam moc a jsou zbastlené, tak co s tím? Co udělá v zoufalosti takovej člověk jako první, protože na něj tlačí vedení, že appka je pomalá? Na všechno vytvoří accessor, protože četl, že to je taky to DI.

Přinese to víc problémů než užitku :)

Editoval Filip Procházka (13. 3. 2013 13:43)

vvoody
Člen | 910
+
0
-

Filip, prečo chceš aby každá cvičená opica, ktorá dostane do ruky nette, naprogramovala ukážkovú aplikáciu? Nemyslím si že je možné naprogramovať framework tak, aby toto dokázal. Zrušíme celý DIC len preto, lebo ho časť programátorov používa ako service locator? (blbý príklad? :D)

Tharos
Člen | 1030
+
0
-

Pro těch pár tříd, jejichž instance se vytvářejí draze…

Mně by se skoro víc líbilo, kdyby Nette umělo pro takové třídy vygenerovat regulérní proxy třídu. Fungovalo by to zhruba následovně:

  1. V konfiguraci u definice služby jen navíc uvedu, že k ní chci vytvořit proxy třídu a tu chci i autowirovat.
  2. Nette ověří, že k dané službě existuje interface. Pokud ne, kompilace skončí chybou. Psaní (a údržba) takového interface je pruda, ale v tomto případě by to mohl být vhodný filtr pro to, aby nebylo této funcionality nadužíváno. Zkrátka: chceš-li opravdu s touhle službou pracovat lazy způsobem, nadefinuj pro ni interface, jinak máš smůlu.
  3. Nette na základě toho interface vygeneruje proxy třídu s lazy vytvořením instance zastupované třídy až v momentě, kdy je to zapotřebí.
  4. V presenterech a všude jinde se taková služba bude autowirovat přes interface. Zkrátka uvedu, že nechci Service\Articles ale Service\IArticles. A kontejner mi u takových služeb předá proxy.
  5. V presenteru (a všude jinde) pak s danou službou budu pracovat úplně normálním způsobem (ani nebudu vědět, že pracuji s proxy implementací).

Co si myslíte o tomhle?

Edit: Teď jsem si teprve pořádně všiml tohohle:

David Grudl napsal(a):
Pokud mi napíše interface, rád mu tu implementaci vygeneruju: řešení pak bude čisté.

No, když existuje poctivě napsané interface, tak je skoro zbytečné generovat nějaký accessor, ale je lepší vygenerovat rovnou proxy, nebo ne? Rozhodně to zjednoduší používání ($this->service->doSomething() namísto $this->serviceAccessor->get()->doSomething()).

Editoval Tharos (13. 3. 2013 14:51)

hrach
Člen | 1834
+
0
-

Nemůžu se než podivovat Hosiplanovu přístupu. Pořád bojuje za nějakou teoretickou čistotu v Nette, ale v praxi všichni víme, že je nepoužitelná. Prasit se bude vždy, Hosiplan to dělá, jen na to nemá koule to hodit do Kdyby. (hosiplan/nette-autowire-properties)


  1. Je naprosto legitimní vyžadovat lazy přístup:
    1. Automatické proxy mi přijde jako totální blbost a magie.
    2. Accessory mi přijdou jako dobré řešení
      • jejich hnusná syntaxe (->get()) mě dostatečně odradí, abych je nepoužíval všude
      • v ostatní případech si nechám injectnout rovnou objekt
  2. Proč je nutné vyžadovat lazy přístup:
    1. cyklická závislost:
      • mějme dva objekty, které navzájem se občas využívají, typicky mějme dvě fasády. Třeba UsersFacade a ArticlesFacade. Dokážu si vcelku jednoduše představit jejich cyklickou závislost. A pokud se vám to nějak nezdá, představte si aplikaci, která ma 30 fasád, a z fleku mi tu odpřisáhněte, že nikdy nemůže nastat cyklická závislost. Jak by se korektně měli injektovat služby v DICu, např. přes constructor, když nebudou lazy?
    2. dominová závislost:
      • mějme opět fasády: jak správně už padlo, vytvoření jednoho objektu nic nestojí. Ale mám v aplikaci 30 fasád, kdy většina zavisí na jedné a více fasádách. Tady už vůbec není zanedbatelné vytvoření všech 30 tříd. Navíc, z principu DI já ani netuším, jak daleko mi domino dojede. Fasáda může být vyžadovat cache. A tato cache může při vytvoření něco invalidovat, připojovat se k memcache serveru, … Čím víc budu mít specializovaných malých tříd, tím více bude dominový efekt zřetelnější a méně předvídatelný. Prosím uvědomte si to.

Shrnutí: Aktuální stav JE a po implementování tohoto i BUDE horší méně pohodlný, než se statickým peklem. Když už jsme si celé aplikace přepsali to ->context->session a nic tím navíc nezískali, rád bych, aby cíl byl vysoký a nebyli spokojeni s aktuálním stavem injectSession(), ale byli ještě pohodlnější a přitom čistí. Občas se musí udělat kompromis. @inject na public property je naprosto čistý. Automatický accessor je naprosto čistý. To, že se něco může zneužít není důvod proti tomu samotnému. Já se ptám, jak udělat accessory pohodlnějšími? Co třeba přidat __invoke()? (Bohužel myslím nebude fungovat na property. $this->pdo()->...)

hrach
Člen | 1834
+
0
-

Tharos napsal(a):

David Grudl napsal(a):
Pokud mi napíše interface, rád mu tu implementaci vygeneruju: řešení pak bude čisté.

No, když existuje poctivě napsané interface, tak je skoro zbytečné generovat nějaký accessor, ale je lepší vygenerovat rovnou proxy, nebo ne? Rozhodně to zjednoduší používání ($this->service->doSomething() namísto $this->serviceAccessor->get()->doSomething()).

No je trochu rozdil psat interface na getter a celou třídu.

Tharos
Člen | 1030
+
0
-

hrach napsal(a):

  1. Automatické proxy mi přijde jako totální blbost a magie.

Co je na proxy magického? IMHO je to naprosto čistá aplikace programování proti rozhraní.

Mohl bys to prosím více rozvést? Nemyslím to vůbec ve zlém, fakt by mě zajímalo, co přesně Tě k takovému závěru vede.

Honza Marek
Člen | 1664
+
0
-

David Grudl napsal(a):

A skutečně: new stdClass (obyčejný objekt) je levnější, než new PDO (obyčejný objekt).

new PDO není obyčejný, ale neobyčejně zprasený objekt. Naštěstí ani PDO nepotřebuje lazy přístup, protože je beztak vytvořeno při každém requestu, protože všechny stránky webu potřebují databázi.

Pokud mě chceš o něčem přesvědčit, chce to lepší příklad ;)

redhead
Člen | 1313
+
0
-

Souhlasím s @Tharosem. Generování proxy proti rozhraní mi přijde daleko lepší než mít accessory. Když interface nebude, vyhodit výjimku. Naprosto dokonalé a neprůstřelné řešení, které navíc bude vést lidi (začátečníky) k programování proti rozhraní.

@inject nad public property OK. Nejspíš to používat nebudu, protože i já bych se z lenosti dokopal k prasení. Ale prosím accessory ne-e!

David Grudl
Nette Core | 8099
+
0
-
  1. accessor je podmnožina generovaných factory, jen přidává omezení, že vždy vrací tentýž objekt. Ve smyslu výrokové logiky, jakákoliv námitka proti accessoru je námitkou proti generovaným factory. Takže prosím, přestaňte používat termín accessor a všechny námitky adresujte přímo proti factories. Třeba to rozjasní diskusi.
  2. proxy je legitimní pattern, ale využívá se spíš při testování, takže DI schopnost je generovat nemá. Bohužel PHP neumí implicitní rozhraní a v případě např. PDO bychom si stejně nepomohli.
Filip Procházka
Moderator | 4668
+
0
-

hrach napsal(a):

Nemůžu se než podivovat Hosiplanovu přístupu. Pořád bojuje za nějakou teoretickou čistotu v Nette, ale v praxi všichni víme, že je nepoužitelná. Prasit se bude vždy, Hosiplan to dělá, jen na to nemá koule to hodit do Kdyby. (hosiplan/nette-autowire-properties)

Tady bych se s dovolením důrazně ohradil!

  1. Zpohodlnění presenterů mám v plánu dát do Kdyby – jen není čas
  2. Teoretickou čistotu praktikuju dnes a denně a nepřekvapivě funguje i v praxi

Jak už jsem opakoval snad 100×

  • V presenterech service locator, nebo nějaká forma autowire – proč ne.
  • V modelech? Nikdy.

Je naprosto legitimní vyžadovat lazy přístup:

Souhlasím.

Automatické proxy mi přijde jako totální blbost a magie.

Pokud napíšeš třídu čistě, tak ne. Na druhou stranu, nejsem fanda proxy patternu, ale má svoje místo například v Doctrine. Tady bych si tím nebyl úplně jistý.

Accessory mi přijdou jako dobré řešení

Accessory jsou zbytečné. Tam kde objekt bude „heavy“ (například PDO), tak ho owrapuju do třídy, které předám například jen pár stringů s údaji pro připojení do databáze (pro PDO) a vytvořím ho až když je potřeba. To že se PDO dědí je prostě chyba návrhu, která v prvopočátku nikoho nenapadla, nebo mu nijak extra nevadila. Na druhou stranu, reálně stránek, které databázi nepotřebují je naprosté minimum.

Vzor dekorátor určitě znáš.

cyklická závislost:
… třeba UsersFacade a ArticlesFacade. …

Co místo cyklické závislosti vytvořit třetí objekt, který bude řešit věci, u kterých jsi se nedokázal rozhodnout, kam je dát? Závislost ti zmizí. Klidně vytvářej tisíce tříd, já to tak dělám a funguje to ;)

Reálné příklady, kdy se tomu nejde vyhnout (narazil jsem na ně), řeším pak service locatorem. Ale těch je minimum. Navíc to vždy napíšu tak, aby se to konfigurovalo, jako by se žádný service locator nepoužíval, ale je pouze optimalizací, která mimo jiné vyřešila i cyklickou závislost.

Čím víc budu mít specializovaných malých tříd, tím více bude dominový efekt zřetelnější a méně předvídatelný. Prosím uvědomte si to.

No a ono je to přesně naopak, když si umíš graf objektů navrhnout.

@inject na public property je naprosto čistý.

Souhlasím.

Automatický accessor je naprosto čistý.

Tomuhle bohužel též nejde nic vytknout. Na druhou stranu, jeho použití se jde vždy vyhnout a proto ho nepovažuji za best practice ani za fallback practise :P

Editoval Filip Procházka (13. 3. 2013 16:30)

Tharos
Člen | 1030
+
0
-

David Grudl:

1. S tímhle tvrzením bych byl opatrný. Mně obojí přijde jako něco docela jiného.

A tu výrokovou logiku jsi trochu obrátil, nemyslíš? :) Škodovky jsou podmnožinou aut. Nemám rád Škodovky ⇒ nemám rád auta? :)

Nemám jedinou námitku proti vzoru factory, mám jen námitky proti accessoru. A necítím se nijak schizofrenní.

2. Navrhuji z diskuze úplně vypustit úvahy nad PDO. Kdo v dnešní frameworkové době přímo vytváří instanci PDO?

redhead
Člen | 1313
+
0
-

No když už budu chtít lazy nad PDO, tak si ten accessor můžu napsat sám. Přijde mi, že tu obhajuješ accessor jen kvůli PDO a ničemu jinému, protože PDO je prostě fuj a dlouho se vytváří.

Kdybych přece jen chtěl mít rozhraní pro PDO je nejjednoduší podědit nebo udělat kompozici do vlastní třídy a udělat vlastní interface. Beng! a můžu mít proxy. A zas tolik práce to nedá.

EDIT: @Hosiplan to napsal dřív a lépe.

Editoval redhead (13. 3. 2013 15:21)

redhead
Člen | 1313
+
0
-

@Hosiplan: také nejsem fanda proxy, protože mě to štve psát. Pokud je ale Nette bude generovat, tak ani nebudu vědět, že nějaké jsou! Pro mě je to daleko lepší případ než Accessor. Neboť o tom budu vědět a budu ho muset používat (místo objektu, který chci).

Co se týče Factory tříd, Davide, já je v kódu používám jen minimálně (myslím tedy vlastní továrny), např. pro výrobu objektu ze složitější polymorfické struktury, který je definovaný nějakým klíčem (většinou se uvnitř skrývá switch).

Btw. kde jste vůbec vzali tento Accessor pattern? Nevzpomínám si, že bych o něm četl v knihách, a Google ví jen o getter metodách (což je mimochodem jiný výraz pro accessor – tedy ten zaběhnutý výraz, jehož smysl tu nabývá nových rozměrů). Pokud to vychází jen z oněch článků od Davida, tak mi přijde, že David řeší něco, co už pár let existuje a funguje to, ale dělá to po svém a blbě.

Pokud bych chtěl optimalizovat výkon, tak v případě Nette bych se díval jinam než zrovna na lazy vytváření objektů (já vím, že odpovědí je „pošli pull-request“).

EDIT: V článku se píše, že testování vede k lepšímu návrhu. Ehm? Teď, když budu chtít testovat s (třeba mocknutou) závislostí, budu muset vytvořit accessor, který mi tuto závislost teprve vrátí. Místo toho abych jen udělal mock a předal.

Editoval redhead (13. 3. 2013 15:57)

redhead
Člen | 1313
+
0
-

Další case, který mluví proti accessorům: nějaká závislost se nyní vytváří dlouho nebo je jinak náročná. Po pár měsících refaktoruji vnitřní kód nebo použiji jinou implementaci a vytváření se stane zanedbatelně náročné – v tuto chvíli už je tedy lazy inicializace pomocí accessorů zbytečná. Jenže všude, kde teď třídu používám budu mít kód $depAccessor->get()->do(); a budu muset přepisovat mnohem víc kódu (odstranit všechny get() volání, měnit type hinty, přejmenovávat proměnné – všude, VŠUDE). Hell!

Tharos
Člen | 1030
+
0
-

redhead: Tvoje velmi trefné postřehy narážejí v důsledku na něco, co se kdysi také řešilo ve flame pod Davidovo články (ve kterých se zrodil pojem „accessor“, není to žádný vzor). Má presenter (respektive nějaká modelová třída) rozhodovat o tom, jakým způsobem vznikne služba, kterou potřebuje? Část lidí tvrdila, že rozhodně ne, David tvrdil, že klidně ano. Řadím se do první skupiny – tohle přece má být presenteru úplně fuk.

redhead
Člen | 1313
+
0
-

Jistě, proč by to měl vědět? Presenteru to má bejt naprosto fuk. On totiž sám neví, co je a co není potřeba inicializovat lazy, co je a co není náročné. Stejně tak i modelové třídy. To ví přece někdo jiný a jsme zpátky u poučky DI, tak jak ji trefně napsal ve svém článku David.

Pokud se něco změní a lazy inicializace nebude potřeba, nastává stejný hell (vlastně o dost větší) jako u ne-DI aplikace, kde je všude nadrátované new Class a my potřebujeme najednou použít Class2.

David Grudl
Nette Core | 8099
+
0
-

Jistě, proč by to měl vědět? Presenteru to má bejt naprosto fuk. On totiž sám neví, co je a co není potřeba inicializovat lazy, co je a co není náročné.

Zcela souhlasím a nikdy jsem netvrdil opak. Co naopak objekt ví, tak že určitou službu potřebuje jen občas (třeba při vzniku chyby apod.). Ovšem ten, kdo objekt vytváří, netuší, zda služba bude potřeba, to se totiž ukáže až později.

Situaci lze řešit tak, že

  1. objekt to neřeší a služba se předá vždycky, protože režie na její vytvoření je menší, než jiné cesty
  2. objekt to neřeší a předá se proxy, neboť režie na vytvoření služby je řádově větší
  3. objekt to řeší a místo služby si nechá předat továrnu, která, až v případě potřeby, službu vyrobí

Všechny tři cesty jsou zcela korektní a podle situace zvolím tu správnou. Dogmatické odsuzování (ať už pod zmíněnými články nebo tady) některé z cest je fakt hloupé.

A nakonec: pojem accessor slouží jen pro odlišení situace, kdy továrna nedovoluje opakované vytváření nového objektu a pracuje se stále s týmž objektem. Že není jinde používán není rozhodně moje chyba. Preferuju přesnější názvosloví.

David Grudl
Nette Core | 8099
+
0
-

A pokud chcete konkrétní příklad, tak třeba Latte se načítá, instancuje a konfiguruje až ve chvíli, kdy se při vykreslování šablon zjistí, že některá z nich není zkompilovaná v cache. Což je třeba v jednom případě z milionu požadavků.

redhead
Člen | 1313
+
0
-

Davide promiň, ale nic z toho, co si teď napsal, neargumentuje proti výše uvedeným důvodům proti accessorům. To, že se Latte instanciuje a konfiguruje až když je potřeba je správně. Proti tomu nic nemám, stejně tak nic nemám obecně proti lazy inicializaci (pokud je nutná a ty případy samozřejmě jsou, jako třeba právě Latte). Ale accessory, tak jak si je podal ty, jsou prostě špatná cesta.

Je vidět, že je tu i jakési pokřivené myšlení ohledně továren. Pokud má nějaká třída závislost na objektu, který má něco vytvářet a třída s tím dále pak pracuje, je správné použít továrnu. Pokud je, ale k činnosti třídy potřeba objekt (a je jedno kdy a jestli vůbec) tak předám prostě ten objekt, na kterém třída závisí (je už jedno jestli uvnitř je lazy inicializace – proxy – nebo není, nezajímá mě to). Třída nebude závislá na nějakém middle-man, ale bude závislá na objektu, který skutečně potřebuje (vždy, někdy, nebo vůbec, to už je jedno). Je lépe testovatelná a je průhlednější.

Ještě jednou, je tu rozdíl mezi závislostí na „továrně na vytváření objektu“ a na samotném „objektu“.

Příklad: pokud mám presenter, který vyrábí články, je asi dobré mít na články továrnu, ať už kvůli nezadrátování new Article v kódu, nebo i pro počáteční konfiguraci (např. datum publikování). To je naprosto legitimní důvod pro použití továrny. Ne však, pokud presenter potřebuje k běhu službu, která vrací seznam všech článků, které chci zobrazit. Pak chci předat nějakou fasádu. Nikoliv továrnu na fasádu (přejmenovanou na accessor). To je prostě špatně, nehledě na důvody, které jsem řekl výše.

Pokud je tvorba té fasády drahá, obalím ji do proxy a k žádné změně nedojde (nikde v kódu).

Obávám se také, že to bude vést k předčasné optimalizaci – radši udělám lazy vše od začátku, abych případně nemusel přepisovat celou aplikaci, když bych musel později použít accessor kvůli pomalé třídě.

ad dogma: Je to jakoby si řekl, že všichni ti, co píšou knihy o designu a architektuře aplikací, nikdy opravdovou aplikaci nenaprogramovali, že se furt jen pídí po tom ultimátním dobru, čistotě kódu a akademické správnosti a nekoukají na praktičnost či použitelnost v dlouhodobém horizontu. Omyl.

Editoval redhead (13. 3. 2013 20:19)

jasir
Člen | 746
+
0
-

Jiný pohled na accessor je, že je to vlastně service lokátor – pro jednu konkrétní službu. A to je moc šikovné (protože x-krát mám chuť si ten container poslat :-P )

Pokud mám problém, že se mi nelíbí $this->myServiceAccessor->get(), obalím si přístup do `

function getMyService()
{
    return $this->myServiceAccessor->get();
}

Což *stejně obyčejně dělám*. Rozdíl proti factory a Accessor je ten, že nemusím psát:

function getMyLazyService() {
  if ($this->myLazyService === NULL) {
     $this->myLazyService = $this->myLazyServiceFactory->create();
  }
  return $this->myLazyService;
}

Já jsem pro.

Editoval jasir (13. 3. 2013 20:24)

jasir
Člen | 746
+
0
-

redhead napsal(a):
Ještě jednou, je tu rozdíl mezi závislostí na „továrně na vytváření objektu“ a na samotném „objektu“.

Ano. A pak můžu mít závislost na service lokátoru (čti – accessoru) – protože lazy, protože to není továrna (objekt se vytvoří jen jednou). Zcela ok.

Editoval jasir (13. 3. 2013 20:25)

redhead
Člen | 1313
+
0
-

jasir napsal(a):
Ano. A pak můžu mít závislost na service lokátoru (čti – accessoru) – protože lazy, protože to není továrna (objekt se vytvoří jen jednou). Zcela ok.

Já ale nechci mít třídu závislou na service lokátoru, ten já pro běh mého objektu nepotřebuji. Třídu chci mít závislou na objektu, se kterým potřebuju pracovat. Service lokátor je další (viditelný) middle-man stejně tak jako je teď context.

David Grudl
Nette Core | 8099
+
0
-

btw, accessor existuje v Nette už dávno. Stačí v příkladu CD-collection přidat rozhraní

interface DatabaseAccessor
{
	/** @return Nette\Database\Connection */
	function create();
}

zaregistrovat je do konfigu

nette:
	database:
		dsn: "sqlite:%appDir%/model/demo.db3"

factories:
	db:
		implement: DatabaseAccessor
		create: @Nette\Database\Connection

a použít třeba v Authenticator.php:

	public function __construct(DatabaseAccessor $database)
	{
		$this->database = $database->create();
	}
stekycz
Člen | 152
+
0
-

Anotace @inject pro public atributy určitě.

Jsem pro Accessory za následujících podmínek:

  • Accessor mi znatelně ušetří psaní kódu oproti proxy nebo vlastní implementaci.
  • Někde v dokumentaci bude jediná stránka, na které bude srozumitelně popsáno jejich použití a použití i dalších zde uvedených postupů, včetně rozlišení, kdy a jak má být který použit. I kdyby jen v Planette. To by začátečníkům mohlo pomoci.

Navíc začátečník bude mít podle mě mnohem více jiných věcí, které bude a může prasit.

redhead
Člen | 1313
+
0
-

David: Nahradil si předání závislosti, kterou autentikátor potřebuje, za něco co tu závislost vytváří. Tedy ze závislosti na objektu si udělal závislost na dodavateli toho objektu. V tomhle případě to ještě tolik nepálí. Kdybys ale tu databázi teď použil i v jiných třídách, budeš ji v každé zase vytvářet? Proč? Proč se o to vytváření starají tyto třídy?

Editoval redhead (13. 3. 2013 20:54)

Filip Procházka
Moderator | 4668
+
0
-

@redhead: David pomocí „triku“ docílil, že se použije jiná service jako factory a vždy se proto vrací stejná instance (protože ta service už ji má vytvořenou a DIC si ji drží).

Editoval Filip Procházka (13. 3. 2013 21:00)

redhead
Člen | 1313
+
0
-

Ok, zajímavé názvosloví objektu vracející jedinou instanci (factory a create), sorry prolít jsem to moc rychle, čekal bych slovo singleton (ne nemyslím statický singleton). Nicméně, proč tedy rovnou nepředat onu instanci a musí se volat nějaká metoda create(), která mě od závislosti bůhví proč odstiňuje? Vytváří se snad lazy? Zajímá mě to? Ne. Proxy? Jsme tam kde jsme byli.

Mimochodem, boží argument pro ospravedlnění accessorů, že už to v Nette funguje!

Pořád čekám, až vypadne nějaký pádný argument.

Editoval redhead (13. 3. 2013 21:18)

David Grudl
Nette Core | 8099
+
0
-

redhead napsal(a):

Pořád čekám, až vypadne nějaký pádný argument.

Už jsem všechno sepsal, víc toho ode mě nebude. Snad jen dodám, že „obalit do proxy“ se lehce napíše, ale lze toho dosáhnout jen za cenu značných kompromisů a prakticky vůbec ne ručně.

maryo
Člen | 15
+
0
-

Taky mi přijde proxy pohodlnější.
Co jsou ty značný kompromisy? Já bych reflexí prošel public metody tý třídy kterou by ta proxy zastupovala a implementoval bych stejný rozhraní. Nejspíš mi teda něco nedochází.

EDIT: Je fakt, že už by to ale nebyla instance tý stejný třídy, to by se muselo udělat děděním a tam je problém s final (jak třídy, tak metod), ale to je potom asi jediná překážka, ne?.

Editoval maryo (14. 3. 2013 0:23)

David Grudl
Nette Core | 8099
+
0
-

A to nestačí?

maryo
Člen | 15
+
0
-

Final skoro nepoužívám, ale tak… možná to i stačí :)…

redhead
Člen | 1313
+
0
-

Pokud tím chceš ospravedlnit špatný návrh, tak prosím. Mělo by to napravovat automaticky Nette? Neřekl bych. (mimochodem, přidat interface pro třídu, na kterou nemůžu šáhnout, je prkotina)

Stejně si myslím, že takových případů, kdy je potřeba ať už proxy nebo accessor má být (a zpravidla taky je, pokud neprasím presenter) minimum a tedy, že i automatické generování (proxy nebo accessoru) je zbytečná feature. Já ani accessor ani proxy v Nette ještě nepoužil a žádný bottle-neck se mi nikde nevyskytl. Po optimalizaci výkonu bych pátral jinde, protože tady problém není, nebo není tak závažný. A když už bude jó potřeba, tak si proxy vyrobím ručně.

David Grudl
Nette Core | 8099
+
0
-

Ano, takových případů je naprosté marginální minimum.

redhead
Člen | 1313
+
0
-

Takže chceš nahradit divnost bez interface, kterou by napsal ručně uživatel, za jinou divnost, kterou vygeneruje Nette. Nehledě na to, že případů, kdy je to potřeba, je minimum. Nějak mi nedochází, proč je tohle v Nette potřeba. A pokud uživatel nedokáže navrhnout aplikaci správně nebo nemá znalosti, tak nejspíš i accessory bude používat blbě (viz co psal Filip na začátku). Snaha byla, máš svou hlavu.

David Grudl
Nette Core | 8099
+
0
-

Já tvrdím, že uživatel je vůbec používat nebude, proč by dělal něco extra složitě? Ale už fakt nemám chuť diskutovat, řekneme si za rok.

paranoiq
Člen | 392
+
0
-

David Grudl napsal(a):

Ano, takových případů je naprosté marginální minimum.

tady jsem se nějak zacyklil. zdá se mi, že debata jde stále dokola…

Filip Procházka
Moderator | 4668
+
0
-

Já bych to viděl tak, že každý řekl svůj názor, David si to nakonec implementoval sám a čas ukáže, jestli to byl dobrý nápad :)

David Grudl
Nette Core | 8099
+
0
-

Diskuse je o tom, že předat si továrnu, která vrací pokaždé nový objekt, je ok, ale předat si továrnu, která vrací stále tentýž objekt, je čiré zvěrstvo ;)