Jak správně injektovat modely
- kleinpetr
- Člen | 480
Ahoj,
snažím se nějak naladit na best practice řešení a zrovna jsem přemýšlel nad tím jak je vlastně správné injektovat. V nějakých projektech injektuji např. databázové modely vždy v presenteru ve kterém model potřebuji, ale zase z hlediska přehlednosti a univerzálnosti jsem začal injektovat většinu věcí už někde v předkovi, např BasePresenteru, jak je to s výkonem nebo co je vlastně lepší řešení ?
Díky za rady a tipy :)
- Oli
- Člen | 1215
Každá třída (teda i presenter) by měla mít jen takový závislosti,
jaký potřebuje. Proto injectovat v BasePresenteru není dobrej nápad. Kdy je
vhodný injectovat v BasePresenteru je, pokud tu službu potřebují všechny
presentery (typicky, když máš menu z databáze). Pro injectování
v potomcích BasePresenteru je vhodné použít inject metodu nebo
@inject anotaci konstruktor.
Edit: Pro injectování v BasePresenteru inject metodu nebo @inject anotaci, protože jinak by jsi musel volat pokaždé parent::__construct, což by jsi měl vždy, ale kdo to dělá? :-)
Editoval Oli (7. 12. 2015 0:27)
- Šaman
- Člen | 2658
V koncových presenterech se doporučuje používat konstruktor, stejně
jako v jakékoliv běžné třídě. Ovšem anotace je kratší.
V abstraktních předcích se doporučuje používat anotaci, nebo inject
metodu, aby zůstal konstruktor volný pro ty koncové presentery.
Taky se doporučuje, aby každý presenter byl buď abstract, nebo final, což souvisí i s tím popsaným výše.
- Šaman
- Člen | 2658
Všechny presentery, které jsou určené k tomu, aby se znich dědilo by
měly být abstract (i ten SecuredPresenter). To abstract
a
final
nedělá nic, než že při pokusu o nesprávné použití
vyhodí výjimku (z abstract nevytvoří instanci a final nemůže být uveden
jako předek). Je to jen pro pořádek.
Rozdíl mezi předáváním konstruktorem a anotací je zásadní – pokud předáváš povinnou závislost, měla by jít přes konstruktor. Pokud není něco k dispozici, tak se to sekne hned v konstruktoru a ani se nevytvoři instace závislé třídy (v tomto případě presenteru).
Nepovinné závisloti by měly jít předávat metodou – klasickým
setterem, ale v Nette se dá předání zautomatizovat, pokud se ten setter
pojmenuje inject…
.
A anotace je trochu fuj – technicky je to předávání závisloti do veřejné proměnné (takže ti to může teoreticky zvenku kdokoliv přepsat). Porušuje to zapouzdření a samozřejmě se může vytvořit instance i bez této závislosti. Připsáním anotace a typu si to Nette zpracují tak, že ihned po vytvoření instance se závislost nastaví, ale i tak je to běžným zdrojem chyb (komentář s jednou hvězdičkou, private/protected property) a pak se chyba projeví až když je daná závislost potřeba (takže na špatném místě – chyba je v definici property a špatně napsané anotaci). Tohle všechno se ti při předávání závislostí konstruktorem (které je mimo presentery jediné podporované pro automatický inject) nestane. Pokud se vytvoří instance, tak i se všemi závislostmi a pokud se nevytvoří, tak to zařve už v konstruktoru.
Anotace inject má jedinou výhodu, která jí ale stačíla k tomu, aby se stala dominantním způsobem v presenterech. Je s ní nejméně psaní :)
Edit: Odmazal jsem zmínku o metodách a akcích v abstraktním presenteru. Zapsat tam samozřejmě jdou, ale volat se musí až na konkrétním potomkovi.
Editoval Šaman (7. 12. 2015 0:55)
- Šaman
- Člen | 2658
Zajímavé povídání na toto téma je tady a úvaha k implementaci v Nette tady (ale je to 4 roky staré! – ber to jen jako úvahu nad výhody/nevýhody jednotlivých možností).
A zkusím se zeptat přímo nepovolanějšího @DavidGrudl : Jak je to aktuálně s předáváním závislostí presenteru konstruktorem? V odkazovaném článku je popis situace, kdy nastane špatně odhalitelná chyba, tím spíš že bude vzácná (v konstruktoru potomka chci sahat na závislost předka, která ale ještě není nastavená). Tenhle problém trvá? Takže v presenterech, ani finálních, není ideální používat konstruktor, nebo je to naopak nejčistčí způsob (jak jsem si doteď myslel)? Díky.
Editoval Šaman (7. 12. 2015 1:09)
- David Grudl
- Nette Core | 8215
V presenterech je ideální předávat závislosti konstruktorem (a krom
předávání tam pokudmožno nic jiného nedělat), ale klidně se dá
používat i anotace @inject
. Nebo metody inject
,
pokud se nejedná o finální presenter.
- kleinpetr
- Člen | 480
@Šaman Ještě se zeptám, teď jsem narazil na situaci, kdy
potřebuji např. do BasePresenteru předat nějakou továrnu na form, který je
v layoutě na všech presenterech. Ale jelikož jsem začal používat
předávání konstruktorem v jeho potomcích tak kdybych chtěl
i u BasePresenteru předat konstruktorem, musel bych u všech potomků
upravit parent::__construct(...)
Což se mi zdá blbý.. Jak tedy
nejlépe řešit tuhle situaci (když nechci použít inject) ? Díky
- castamir
- Člen | 629
Přesně taková situace je tzv. dependency hell, tj. kvůli jediné změně
závislostí musíš upravit všechny potomky. Abys to dělat nemusel, tak
existují právě anotace @inject
a metody inject*
,
které to za tebe vyřeší bez nutnosti upravovat potomky.
Pokud si tedy chceš zajistit tu továrničku pro všechny potomky BasePresenteru, tak v BasePresenteru použij inject a v potomcích ponech předání závislostí konstruktorem.
- Šaman
- Člen | 2658
V tomhle případě inject metodu, nebo anotaci použij. Přesně na tohle
je dělaná.
A to, že nechceš u všech potomků upravovat konstruktor, to je přesně ten
Constructor
hell, kvůli kterému inject metody a anotace vznikly. (A proto funguje jen
v presenterech, protože v jiných třídách tento problém není tak
typický).
- Šaman
- Člen | 2658
To je hodně dobrý tahák.
Trochu nesouhlasím jen s poznámkou 2, viz tohle vlákno. (Edit:
Zmíněná poznámka byla odstraněná.) Pokud chci psát co nejčistěji, je
inject metoda vhodnější, než anotace inject, protože nemusím porušovat
zapouzdření (public property). Inject metoda má navíc stejnou strukturu,
jako předávání konstruktorem a pokud ji chápu, jako odložený konstruktor,
tak mi zůstane veškeré injectování konzistentní. (Ve většině ukázek
jsou pro každou závislost samostatné injectFoo()
metody, ale
velmi se mi osvědčilo mít za konstruktorem jedinou inject metodu se všemi
závislostmi – tedy stejně, jako kdybych injectoval konstruktorem.)
Editoval Šaman (2. 1. 2016 12:19)
- David Matějka
- Moderator | 6445
@Šaman zvazoval jsem, ze tu 2. poznamku odeberu, tak teda jo, at si kazdej vybere :)
- Šaman
- Člen | 2658
@DavidMatějka: Super, tenhle tahák budu šířit dál.
Ještě bys mohl pro pořádek do poznámky 1 přidat slovo „vhodné“. Aby si někdo nemyslel, že v BasePresenteru předávání konstruktorem nebude fungovat. Bude, jen je to nepraktické.
Resp. možná bych to přeformuloval na „nevhodné v abstraktních Base* presenterech“.
Editoval Šaman (2. 1. 2016 14:43)
- Jiří Nápravník
- Člen | 710
Ok, přepíšu taky na konstruktor vše:-) Nicméně zajímá mě, voláte v konstruktoru u presenteru parent::__construct()? Když tam nic nemám, tak to moc nedává smysl, ale nevím jestli je to best practise.
A jak se tváříte ke Kdyby\Autowired – nemyslím ted obdobu inject, ale myslím konkrétně ty továrničky na komponenty.
Editoval Jiří Nápravník (2. 1. 2016 13:18)
- Šaman
- Člen | 2658
IMHO parent::__construct()
volat. Ať už kvůli konzistenci
kódu, ale hlavně pokud se do konstruktoru parenta něco přidá (ač by dle
konvence nemělo), tak to zařve.
Edit: A navíc u presenteru
konstruktor není prázdný, jen se přes něj nepředávají žádné
závislosti.
Editoval Šaman (2. 1. 2016 13:23)
- F.Vesely
- Člen | 369
Jiří Nápravník napsal(a):
A jak se tváříte ke Kdyby\Autowired – nemyslím ted obdobu inject, ale myslím konkrétně ty továrničky na komponenty.
Ja osobne Kdyby\Autowired pouzivam, protoze vsechno jedu pres komponenty, kde uz mam vse v konstruktoru. Pouzivam jeste k tomu live templates v PhPStormu, takze se mi to i pekne pise.
- David Matějka
- Moderator | 6445
Edit: A navíc u presenteru konstruktor není prázdný, jen se přes něj nepředávají žádné závislosti.
Nette s tim pocita, ze se to nezavola: https://api.nette.org/…ter.php.html#170
asi u vseho, u ceho se pocita s dedicnosti (presenter, control, component…), se prakticky nemusi volat rodicovsky konstroktor. spravne by se ale volat mel.