Jak předávat závislosti do presenterů, komponent a jiných služeb?

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

Současná dokumentace nepopisuje téma Dependency Injection zrovna šťastně, věnuje totiž hodně prostoru DI kontejneru, ale nejdůležitější téma, tedy jak předávat závislosti, není popsané dostatečně. Už je, díky Vojto!

Doporučil bych vám proto pustit si přednášku Filipa Procházky či přečíst DI a předávání závislostí a rozhodně článek Vojty Dobeše Tvorba komponent s využitím autowiringu.

Jaké je best practice pro předávání závislostí presenterům, komponentám a jiným službám v Nette?

Nezávisle na frameworku a typu tříd vždy platí totéž:

  • povinné závislosti konstruktorem
  • volitelné buď samostatnou metodou, nebo též konstruktorem

Tečka.


Nicméně existuje situace, kdy je předávání závislostí konstruktorem problematické, tzv. constructor hell, tedy situace, kdy předáváme závislosti konstruktorem určité třídě a zároveň jejímu potomkovi. Tohle nastává často u presenterů, kde je právě zvykem mít nějaký BasePresenter. Jako workaround dává DI kontejner v Nette možnost použít v BasePresenteru místo konstruktoru metodu nazvanou injectBase() (případně jakkoliv jinak, jen musí začínat na inject).

Jde o workaround pro base presentery, takže v jiných situacích metody inject nepoužívejte.

Dále předávání závislostí přes konstruktory nebo jiné metody je otravné v tom, že musíte napsat nějaký rutinní kód. Nutnost psát rutinní kód vždy ukazuje na slabé místo samotného jazyka, jedna ze šancí na zjednodušení byla zamítnuta, nicméně některé IDE ho umí vygenerovat a taktéž DI kontejner ve frameworku nabízí zkratku v podobě @inject anotací. Public (!) proměnnou doplníte anotací např. /** @var Model\UserFacade @inject */ a framework sám do ní vloží službu a vyhnete se psaní konstruktoru nebo inject metody.

Protože to není čisté řešení, funguje opět jen pro presentery. A existuje ještě špinavější hack, property lazy-autowire na steroidech. Jejich používání rozhodně není best practice, ale je to sakra pohodlné.

Ripper
Člen | 56
+
0
-

Jaký je rozdíl mezi anotací @inject a @autowire z Filipova řešení? Jinak video je soukromé a nejde spustit.

tsusanka
Člen | 23
+
0
-

@Ripper myslim, že správnej link na video je https://www.youtube.com/watch?…

David Grudl
Nette Core | 8218
+
0
-

Video už by mělo fungovat. Filipovo řešení funguje i s protected proměnnýma a používá lazy loading, tedy služba se načítá teprve v okamžiku, kdy se k proměnné přistoupí.

David Matějka
Moderator | 6445
+
0
-

@Ripper: @inject zavislosti vklada hned pri vytvareni objektu, @autowire je lazy, z kontejneru se zavislosti vytahnou az kdyz je potrebujes. kdyby/autowired ma navic jeste predavani zavislosti do createComponent* metod

EDIT: jeste si dovolim self promo, cheatsheet o di v nette

Editoval matej21 (15. 5. 2014 14:28)

Šaman
Člen | 2658
+
0
-

Ripper napsal(a):

Jaký je rozdíl mezi anotací @inject a @autowire z Filipova řešení? Jinak video je soukromé a nejde spustit.

Filipovo řešení nevyžaduje, aby byla property public. Řeší to pomocí černé magie. Anotace @inject je obyčená property injection, tedy zápis do public proměnné a jediné, co se nám na ni nelíbí je porušení zapouzdření (na druhou stranu presentery jsou velmi specifické a důsledně je zapouzdřovat nemá až tak smysl).

Šaman
Člen | 2658
+
0
-

@David Grudl: Jediné, co se mi ve stávajícím Nette nedaří uspokojivě vyřešit je jak dostat do BaseControl službu, aniž by se jí museli potomci zabývat?

Editoval Šaman (15. 5. 2014 15:30)

David Matějka
Moderator | 6445
+
0
-

@Šaman: muzes si napsat CompilerExtension, ktery v beforeCompile pozapina u (vsech) sluzeb inject

enumag
Člen | 2118
+
0
-

Když už byl zmíněn článek https://doc.nette.org/…tion/factory, postrádám v něm klíčovou informaci jak té RegistrationControl předat jiný než výchozí IMailer či jinou službu (a nepodařilo se mi toho žádným způsobem docílit).

Editoval enumag (16. 5. 2014 9:49)

Vojtěch Dobeš
Gold Partner | 1316
+
0
-
services:
	-
		implement: IRegistrationControlFactory
		arguments: [..., ..., @myCustomMailer]

	myCustomMailer:
		class: # ...

(neřekl bych, že je to klíčová informace, aplikace obvykle využívají defaultní varianty služeb, ale jako tip by se to tam přidat dalo, souhlasím)

enumag
Člen | 2118
+
0
-

@vojtech.dobes: Díky, tohle funguje. Já to zkoušel takhle:

services:
    - IRegistrationControlFactory( @myCustomMailer ) # neměl jsem jiné argumenty

Což skončilo naprosto prapodivnou chybou protože ta IRegistrationControlFactory nakonec vůbec nevracela to co měla ale kdovíproč vracela ten @myCustomMailer.