Dependency Injection ve 2.1: Anotace @inject

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

(Navazuji na starší příspěvek)

Navrhuji používat v dokumentaci jako preferovaný typ injektování konstruktor a právě anotace @inject. Naopak bych zcela (v příkladech, dokumentaci) opustil metody inject–. Ty vznikly čistě jako workaround kvůli problémům s konstruktorem a dědičností a jejich použití jinde je nešťastné. A protože ho vídám čím dál tím častěji, aby by bylo vhodné mu udělat přítrž.

Anotaci @inject vnímám čistě jako syntax sugar pro rutinní předání závislostí přes konstruktor. Z toho důvodu by měla existovat i varianta pro volitelný inject, zapsaná buď jako @inject? nebo možná ještě lépe jako @var My\Class|NULL @inject.

enumag
Člen | 2118
+
+1
-

Rozhodně @var My\Class|NULL. Ta anotace @var tam musí být v každém případě a bude lepší uživatele nutit zapsat ji správně včetně NULL než zavádět magický otazník.

Editoval enumag (23. 4. 2013 20:32)

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

Pod enumagův příspěvek bych se podepsal.

MartinitCZ
Člen | 580
+
0
-

@**enumag**: +1

Filip Procházka
Moderator | 4668
+
0
-

Já jsem pro a klidně bych z fleku začal přepisovat dokumentaci, ale zase mi někteří budou vyčítat, že chci psát dokumentaci pro nestabilní verzi, nebo dokonce, že bychom měli dokumentaci psát pro 2.0.0 a né pro 2.0.10 (nebo .11 ?).

Majkl578
Moderator | 1364
+
0
-

Já jsem pro a klidně bych z fleku začal přepisovat dokumentaci

Domnívám se, že ve chvíli, kdy 2.1 vstoupí do beta fáze, dokumentace by se měla rozdělit na verzi pro 2.0 a 2.1.

Stále považuji za zásadní problém chybějící podporu use statementů. Mám zkusit připravit pull?

hrach
Člen | 1834
+
0
-

Domnívám se, že ve chvíli, kdy 2.1 vstoupí do beta fáze, dokumentace by se měla rozdělit na verzi pro 2.0 a 2.1.

Idealne aby to bylo na gitu, to se pak bude delat jedna basen :)

Stále považuji za zásadní problém chybějící podporu use statementů. Mám zkusit připravit pull?

Urcite prosim :)

Eda
Backer | 220
+
0
-
  • souhlas. Není důvod používat inject* metody, když máme @inject
  • za podporu use statementu bych se přimlouval :-)
enumag
Člen | 2118
+
0
-

@Majkl578: +1 pro use!

@hrach:

Idealne aby to bylo na gitu, to se pak bude delat jedna basen :)

To by bylo skvělý! +1

Editoval enumag (23. 4. 2013 20:35)

Filip Procházka
Moderator | 4668
+
0
-

@Majkl578 go for it… v reflexi je to vyloženě potřeba :)

duke
Člen | 650
+
0
-

Ohledně @inject, v současné době funguje @inject pouze u public proměnných tříd. Inject* metody umožňují injektování i do private či protected proměnných. Inject* metody tedy v tuto chvíli nelze zcela nahradit anotací @inject. Nebo mi něco uniklo?

Filip Procházka
Moderator | 4668
+
0
-

@inject do private/protected nemá nic společného s DI.

Editoval Filip Procházka (23. 4. 2013 22:32)

duke
Člen | 650
+
0
-

Svůj příspěvek jsem nepsal proto, abych obhajoval @inject do private/protected metod, ale proto, abych se dozvědel, čím chce David nahradit aktuální použití inject* metod (kterým chce učinit přítrž) v případech, kdy tyto metody nastavují private či protected proměnné objektu. Navíc jeho poznámka, že @inject vnímá jako syntaktický cukr pro rutinní předání závislostí přes konstruktor naznačuje možnost, že hodlá podporovat i tuto formu injektáže (neboť konstruktor ji také umožňuje).

Odpověď „@inject do private/protected nemá nic společného s DI“ je pro mě v tomto ohledu naprosto k ničemu. Navíc s ní ani nelze souhlasit, protože to injektáž závislostí (tedy DI) nepochybně je (neříkám že se neprohřešuje proti určitým principům – jde o jakousi násilnou formu DI – a nijak ji nepropaguji), ale je tím, čím je.

Honza Marek
Člen | 1664
+
0
-

Náhražkou inject metod je právě použití public property.

Majkl578
Moderator | 1364
+
0
-

Podle mě by možnost injectu do protected/private měla být přinejmenším konfigurovatelná. V Javě je to běžná, osvědčená, přímočará a jednoduchá praktika, jak do objektu dostat závislost.

@inject do private/protected nemá nic společného s DI.

Nesmysl. DI je to stejně tak jako injectování do public property. Jediný rozdíl je pouze ten, že třída explicitně neuvádí závislosti skrze své API. Dokážu si představit, že v aplikaci bez testů a/nebo v presenterech to kdekdo rád oželí.

Náhražkou inject metod je právě použití public property.

Ne plnohodnotná, jelikož s sebou přináší problémy.
Vyžadování public property je sice hezké z akademického hlediska, ale zároveň to (bez nativních property accessorů) porušuje zapouzdřenost objektu, poněvadž kdokoliv může kdykoliv za běhu (třeba i omylem) změnit hodnotu a objekt se o tom vůbec nedozví.

Šaman
Člen | 2634
+
0
-

Vyžadování public property není hezké z akademického hlediska, právě kvůli porušení zapouzdření.
Přes setter (injector) jsem schopen nějaké základní kontroly, při přímém zápisu do property nikoliv.

Anebo jsem špatně pohopil stávající @inject. Znamená to, že $database, $cache apod budou veřejné property?

Tharos
Člen | 1030
+
0
-

Já teda vnímám pečlivé zapouzdření jako jeden z klíčových aspektů robustního návrhu. Takže pokud by @inject vyžadovalo zrušit zapouzdření, sám bych to rozhodně nepoužíval (nabourání zapouzdření by pro mě byla až příliš vysoká oběť). Pokud by šlo injektovat do privátních proměnných, v např. presenterech bych to určitě používal (za cenu drobného hacku, oželení akademické čistoty a de facto vytvoření závislosti na konkrétním DI kontejneru si významně ušetřím psaní).

Kdykoliv přijdu ke kódu, kde je nějaká veřejná třídní proměnná, tak chvíli tápu, co si o ní mám myslet a co si s ní mohu dovolit dělat. U nějakých „value objektů“ se zorientuji rychle, ale pokud je to instance nějaké „normální třídy“, jak s tou proměnnou mohu pracovat? Mohu tam jen tak něco frknout? Rozbije se to nebo ne? Co když tam frknu null? Nebo úplně jiný typ, než to předpokládá? Poznám vůbec, že jsem to rozbil? Jsou na to testy?

Editoval Tharos (24. 4. 2013 10:41)

enumag
Člen | 2118
+
0
-

Osobně mám s tím public taky problém, inject* metody a zapouzdření je mi příjemnější. Problém je, že kdyby se mělo injectovat do private, muselo by se vyřešit injectování do stejně pojmenovaných private vlastností na různých úrovních dědičnosti. Inject* metody tohle řeší bez problému.

Kvůli tomu zapoudření ty inject metody v presenterech vnímám jako better-practice než anotace. Podobně constructor injection mimo presentery.

Editoval enumag (24. 4. 2013 10:47)

newPOPE
Člen | 648
+
0
-

Public properties mi nepridu ako dobry napad.

Takisto metody inject*. 2–3 zavislosti a mam x riadkov kodu a to som este ani nezacal…

Skor by ma zaujimali moznosti pouzitia (use case-s):

  • Komponenty (konstruktor)
  • Presentery
  • Sluzby (services, servants, …) (konstruktor)
enumag
Člen | 2118
+
0
-

@newPOPE: V podstatě jde pouze o ty presentery. V komponentách i službách je téměř vždy lepší použít constructor-injection, jak jsi už řekl. Osobně inject* metody ani @inject anotace mimo presentery nepoužívám a jejich použití jinde považuji za bad-practice.

Filip Procházka
Moderator | 4668
+
0
-

Majkl578 napsal(a):

@inject do private/protected nemá nic společného s DI.

Nesmysl. DI je to stejně tak jako injectování do public property. Jediný rozdíl je pouze ten, že třída explicitně neuvádí závislosti skrze své API. Dokážu si představit, že v aplikaci bez testů a/nebo v presenterech to kdekdo rád oželí.

Pokud máš aplikaci bez testů, nevidím problém v psaní pár public properties. To je jako umývat okna hořícího domu – nic to nezmění :)

Náhražkou inject metod je právě použití public property.

Ne plnohodnotná, jelikož s sebou přináší problémy.
Vyžadování public property je sice hezké z akademického hlediska, ale zároveň to (bez nativních property accessorů) porušuje zapouzdřenost objektu, poněvadž kdokoliv může kdykoliv za běhu (třeba i omylem) změnit hodnotu a objekt se o tom vůbec nedozví.

Krásně si tady protiřečíš.

  • „třída explicitně neuvádí závislosti“
  • „zároveň to porušuje zapouzdřenost objektu“

Injektování do private/protected zapouzdřenost neporušuje?


Stále mi přijde jako nejlepší řešení tohle.

Editoval Filip Procházka (24. 4. 2013 15:18)

Majkl578
Moderator | 1364
+
0
-

Filip Procházka napsal(a):

Pokud máš aplikaci bez testů, nevidím problém v psaní pár public properties. To je jako umývat okna hořícího domu – nic to nezmění :)

Návrh a implementace aplikace může být kvalitní bez ohledu na to, jestli tam je milion nebo nula testů.

Krásně si tady protiřečíš.

  • „třída explicitně neuvádí závislosti“
  • „zároveň to porušuje zapouzdřenost objektu“

Injektování do private/protected zapouzdřenost neporušuje?

Jelikož se to děje jen a pouze ve chvíli inicializace objektu (tj. bezprostředně po volání konstruktoru), považuji to spíše za součást inicializace objektu, jelikož objekt ještě nemá konzistentní vnitřní stav.

Editoval Majkl578 (24. 4. 2013 15:35)

David Grudl
Nette Core | 8111
+
0
-

Ad public property a porušení zapouzdření: nechci se opakovat, vše najdete tady.

Jen doplním: anotace @inject zapouzdřuje proměnnou na úrovni dohody. Stejně jako anotace @return nekontroluje, zda funkce vrací to, co má, ale věříme tomu. Nebo stejně, jako nekontrolujeme, zda někdo nepřepisuje závislosti uložené v privátních proměnných tím, že by nad objektem znovu zavolal metodu __construct a předal jiné závislosti. Nebo dokonce potlačil E_RECOVERABLE_ERROR a protlačil objekty jiného typu, než je typehint.

Samozřejmě pokud vám úroveň dohody nevyhovuje, použijte privátní proměnné a konstruktor injection.

duke
Člen | 650
+
0
-

Z wikipedie cituji, ohledně zapouzdření: „Zapouzdření v objektech znamená, že k obsahu objektu se nedostane nikdo jiný, než sám vlastník.“

Řešení pomocí public properties znamená, že se k dané proměnné (která je součástí obsahu objektu) dostane kdokoli. Trik s unset popsaný v odkazovaném článku řeší zapouzdření pouze částečně, tj. řeší problém možného následného přepsání zvenčí (zajišťuje, že proměnná je write-once), ale už neřeší problém přístupu pro čtení (znemožňuje její privátnost, tj. kompletní zapouzdření).

Inject* metody kompletní zapouzdření umožňují, byť je s nimi trochu více psaní…

Majkl578
Moderator | 1364
+
0
-

Jen doplním: anotace @inject zapouzdřuje proměnnou na úrovni dohody.

Následujíc tuto myšlenku, můžeme všude a) zrušit gettery a settery ve prospěch public properties, b) zrušit non-public viditelnost metod a označit obojí za dohodu. To je ale přeci nesmysl.

Stejně jako anotace @return nekontroluje, zda funkce vrací to, co má, ale věříme tomu.

To je míchání jablek s hruškama. Nijak to totiž neovlivňuje stav/zapouzdřenost daného objektu – je jen a pouze na volajícím, zda tomu věří nebo ne, a ať se rozhodne jakkoliv, stav volaného objektu se tím nijak nezmění.

Nebo dokonce potlačil E_RECOVERABLE_ERROR a protlačil objekty jiného typu, než je typehint.

Tohle je za normálních okolností neproveditelné a pokud by to někdo dělal, je to jako střílet se do vlastní paty. (http://3v4l.org/UgLuj)
Rozhodně je to, narozdíl např. od neúmyslného zápisu do public property, naprosto okrajový případ (ještě krajnější, než aby si dotyčný upravil hodnotu property reflexí).

Samozřejmě pokud vám úroveň dohody nevyhovuje, použijte privátní proměnné a konstruktor injection.

Stále zapomínáš, že ještě lze použít, po vzoru Javy, kde se to velice osvědčilo, private property a property injection. To je totiž z hlediska napsání nejúspornější a zároveň z hlediska zapouzdřenosti nejbezpečnější cesta, která řeší všechny tebou zmínené problémy (a to i včetně hypotetického – ač nereálného – potlačení E_RECOVERABLE_ERROR nebo opakované volání konstruktoru). ;)

@duke: Vesměs souhlasím.

Editoval Majkl578 (25. 4. 2013 2:32)

Filip Procházka
Moderator | 4668
+
0
-

Majkl578 napsal(a):

Stále zapomínáš, že ještě lze použít, po vzoru Javy, kde se to velice osvědčilo, private property a property injection.

Stále zapomínáš, že to s Dependency Injection nemá nic společného.

David Grudl
Nette Core | 8111
+
0
-

Majkl578 napsal(a):

Stále zapomínáš, že ještě lze použít, po vzoru Javy, kde se to velice osvědčilo, private property a property injection.

Nezapomínám! Jen mi stále PHP při zápisu do private property vyhazuje fatal error. Nejspíš používáš nějakou jinou verzi.

Elijen
Člen | 171
+
0
-

Injectnout private property by šli přes Reflection, ale to je asi ta vůbec nejhorší možnost předání závislostí.

Osobně se mi nejvíc líbí constructor injection v kombinaci se setter injection. U presenteru bych klidně přežil public property s anotací @inject, ale u služeb mi to přijde snad ještě horší než metody inject*.

Jaká je vůbec motivace pro zrušení inject* metod kromě méně psaní v presenterech? Aneb v čem je @inject lepší? To tu podle mě vůbec nezaznělo.

Editoval Elijen (25. 4. 2013 11:17)

Filip Procházka
Moderator | 4668
+
0
-

Imho tenhle příspěvek je stále aktuální. @inject je línější varianta pro inject* metody. Méně psaní.

Každopádně, tahle debata je jenom o presenterech, v modelech a komponentách téměř nemá smysl používat cokoliv jiného, než constructor injection.

pekelnik
Člen | 462
+
0
-

…PHP při zápisu do private property vyhazuje fatal error. Nejspíš používáš nějakou jinou verzi.

Jo! Jmenuje se Nette ;)

Co použít setter-injection made by Nette\Object:

use Nette\Object;
use Nette\Caching\Cache;

class Foo extends Object
{
	/**
	 * @inject
	 * @var Cache
	 */
	private $bar;

	public function getBar()
	{
		return $this->bar;
	}

	public function setBar($bar)
	{
		$this->bar = $bar;
	}
}

Povšimněte si stupidních getterů a setterů…

Editoval pekelnik (25. 4. 2013 15:52)

Honza Marek
Člen | 1664
+
0
-

Když už, tak:

use Nette\Object;
use Nette\Caching\Cache;

class Foo extends Object
{
	private $bar;

	/** @inject */
	public function setBar(Cache $bar)
	{
		$this->bar = $bar;
	}
}
enumag
Člen | 2118
+
+1
-

Což jsme v podstatě tam kde jsme byli – u inject metod. :-)

Šaman
Člen | 2634
+
0
-

Inject metody jsou podle mě dobré řešení, ale vůbec by se nemusely psát, stačilo by je definovat v anotacích a volat je magicky. Sice na úrovni kodu jsou to skryté závislosti, ale každé IDE které umí napovídat si s tím poradí a pokud programátor dostane třídu, má hned nahoře seznam těchto magicky nastavovaných závislostí. Pro mě by to byl přijatelný kompromis.
Rovnou by to mohlo zvládat i obyčejné settery a gettery.

<?php
/**
* @method void injectFoo($foo)
* @method void setBar($bar)
* @method int  getBar()
*/
class FooBar extends Object
{
   /** @var classFoo */
   protected $foo; // proměnná bude přístupná pro zápis a navíc bude automaticky injectovaná
   protected $bar; // proměnná bude přístupná ke čtení i zápisu magicky
   protected $i;   // proměnná není přístupná
}
?>

// edit: doplněna třída, která se má injectovat, díky @castamir
On by ten typ proměnné měl být uveden všude, ale u injectované být musí

// edit2: Viditelnost změněná na protected kvůli nastavování v předkovi. Stejně private nepoužívám kvůli prolémům s děděním, jen jsem použil příklad výše..

Editoval Šaman (27. 4. 2013 10:21)

castamir
Člen | 629
+
0
-

@Šaman chybí ti tam, co se vlastně má injectovat (TYPE_HINT)

Jan Tvrdík
Nette guru | 2595
+
0
-

@Šaman: To by ale vyžadovalo podporu DI ze strany Nette\Object.

stekycz
Člen | 152
+
0
-

Osobně nevidím důvod, proč by pro Presentery měl být problém porušení zapouzdřenosti použitím public property. Zapouzdřenost potřebuji pouze pokud pracuji s danou třídou jako uživatel zvenčí. Pokud ji zvenčí používá jen framework, může mi být v podstatě jedno, jestli používá public property injection nebo použije nějaký setter či konstruktor. Já tyhle metody stejně volat nebudu. Navíc jsem líný a chci psát co nejúsporněji to lze, tedy anotace je asi to nejjednodušší.

Nevím jak kdo tady, ale já zatím neměl potřebu vytvářet si instanci Presenteru sám a přistupovat k ní zvenku. Jedinou výjimkou může být přímé testování Presenterů, o čemž psal nedávno David. V tomhle případě se ale přikláním k @Hosiplanovi, který upřednostňuje testování Presenterů rovnou s celou aplikací pomocí Selenia (jak psal na Twitteru).

Ještě jednou ale upozorňuji, že předpokládám tuto funkcionalitu jen a pouze v Presenterech. Nette\Application\UI\PresenterComponent ani Nette\Application\UI\Control tuto funkcionalitu umožňovat IMHO nemá. Proč? Protože k instancím těchto tříd chci přistupovat zvenčí a vytvářím si jejich instance sám.

Osobně teď používám na jednom projektu od @Hosiplana úpravený BasePresenter pro použití anotace @autowire a jsem spokojen. Jediný rozdíl (alespoň co vím) oproti @inject je v tom, že Filip používá protected property a injektace se provádí až při přístupu k dané službě/property.

Majkl578
Moderator | 1364
+
0
-

David Grudl napsal(a):

Nezapomínám! Jen mi stále PHP při zápisu do private property vyhazuje fatal error. Nejspíš používáš nějakou jinou verzi.

Stačí přeci použít reflexi. ;)

stekycz napsal(a):

Osobně nevidím důvod, proč by pro Presentery měl být problém porušení zapouzdřenosti použitím public property.

Tahle diskuze nicméně není pouze o Presenterech, ale o DI obecně.

Zapouzdřenost potřebuji pouze pokud pracuji s danou třídou jako uživatel zvenčí. Pokud ji zvenčí používá jen framework …

Opravdu? A co například použití presenteru v komponentě nebo šabloně?

Navíc jsem líný a chci psát co nejúsporněji to lze, tedy anotace je asi to nejjednodušší.

Souhlasím. Jako nejúspornější a zároveň nejbezpečnější vychází property injection do non-public property, že? :)

Osobně teď používám na jednom projektu od @Hosiplana úpravený BasePresenter pro použití anotace @autowire a jsem spokojen. Jediný rozdíl (alespoň co vím) oproti @inject je v tom, že Filip používá protected property a injektace se provádí až při přístupu k dané službě/property.

Je krásné vidět, že vlastně i HosipLan, který private property injection odmítá, sám používá její obdobu, dokonce zkombinovanou se zatracovaným service locatorem. ;)

@Šaman: Nemyslím si, že podobné přespříliš magické věci by měly být přímo součástí Nette a už vůbec ne Objectu. Shodou náhod je to jedno z řešení, které jsem i já už dříve navrhoval.


Nicméně jak property injection, tak setter injection (pro nutné závislosti) mají jeden zásadní drawback – inicializují objekt dvoukrokově, tudíž v určitou chvíli se nachází v ne-plně-inicializovaném stavu. Co když například někdo bude vyžadovat závislosti pouze skrze properties a v konstruktoru bude mít nějakou (ač jakkoliv jednoduchou) logiku? S constructor injection by tenhle problém řešit nemusel. Kdysi jsme se o tom s někým bavili a jako možné řešení padlo vytvoření instance bez volání konstruktoru, nasetování vlastností / zavolání inject setterů a až následně zavolání konstruktoru.

Editoval Majkl578 (26. 4. 2013 0:47)

mishak
Člen | 94
+
0
-

@Šaman: private atribut potomka nemůžeš nastavit v jeho rodiči, leda přístupem přes ReflectionProperty.
Pak další problém s použitím soukromých atributů je při vícenásobném dědění, inject pak musíš provést pro celou linii předků.

Public a unset je celkem elegantní řešení a nejspíš jediné možné ostatní zavádějí daleko více okrajových případů, které je třeba řešit.
+1 Pokud bude podporovat i NULL parametry.
Zároveň neúmyslně řeší používání stejných služeb rodičem a potomkem. – Nevím jestli je to správné, nebo jestli je to třeba vůbec řešit.

Pokud jde o inject i mimo presentery z továrny, zde stále zbývá vyřešit kontrolu konzistence objektu, jinak každý test bude muset obsahovat Object::assertRequiredInjections($object). :( Chápu, že to je problém, ale bez vyřešení kontroly samotné řešení, může napáchat více škody (vyžaduje důslednost a nepadá okamžitě při chybném stavu).
Část řešení by byla zavést počítadlo nastavených povinných atributů, ale nepřišel sem na vhodný okamžik života objektu, kdy ho zkontrolovat. Šlo by to řešit přes generovanou proxy pro každou třídu, ale to mi přijde jako kanón na vrabce a navíc je třeba ručně balit nebo továrny (opět ta důslednost).

Editoval mishak (26. 4. 2013 0:57)

David Grudl
Nette Core | 8111
+
0
-

Asi by bylo záhodno povolit metody inject jen pro presentery.

Šaman napsal(a):

Rovnou by to mohlo zvládat i obyčejné settery a gettery.

Tohle už v Nette existuje, viz třeba tady https://github.com/…r/Method.php (ale pravda, je to ve formě konceptu, commitnu full verzi).

enumag
Člen | 2118
+
0
-

Asi by bylo záhodno povolit metody inject jen pro presentery.

Tak to ani náhodou. Dejme tomu pokud by totéž platilo i pro @inject anotace. Nutnost public properties mimo presentery bych opravdu nerozdejchal. Je pravda že mimo presentery inject* ani @inject nepoužívám, ale v případě mnoha závislostí a dědičnosti (ala Presenter) se to může hodit. Samozřejmě v takovém případě je vhodný refactoring, rozbití na více tříd a snížení počtu závislostí, ale to už je druhá věc a nemyslím, že by Nette mimo presentery mělo umět pouze constructor injection.

David Grudl
Nette Core | 8111
+
0
-

DI umí všechny myslitelné druhy injektáže, viz setup.

pekelnik
Člen | 462
+
0
-

@David pravděpodobně jsi myslel DIC – DI umí všechny myslitelné injektáže už z podstaty věci ;)

Rád bych aby se tady diskutující přestali ohánět presentery. O presentery v této diskusi nejde ani okrajově.

Bavíme se tady o tom, zda jako doporučovaný způsob injektáže mimo constructor injection začít tlačit anotace @inject na úkor inject metod a dále o způsobu zápisu volitelné injektáže. To je taková injektáž, která se neprovede pokud závislost (služba) s daným rozhraním (daného typu) neexistuje.

/**
 * @inject @var Cache|NULL
 */
public $cache;

Nic lepšího tady nepadlo. Otazník za anotací nepovažuji za šťastný a vzhledem k tomu, že inject metody byly od začátku pouze workaround zdá se mi toto jako čistější náhrada.

K tématu zapouzdření bych dodal pouze to, že nemůžete chtít všechno najednou. Kromě Majkla. Ten to chce a nestydí se za to!

Mimochodem naprosto perfektní injektáže do private/protected proměnných dosáhnete mnohými dokola omílanou a jinými naprosto opomíjenou constructor injection.

Pro zájemce odkazy na další zajímavé čtení o DI:

Ještě bych rád dodal, že z důvodu zpětné kompatibility by inject metody měly zůstat funkční minimálně někam do verze 2.2. Nevadilo by mi kdyby Nette ve verzi 2.1 generovalo upozornění úrovně E_USER_DEPRECATED pro tyto metody. Rozumný člověk nenechá takovouhle chybu na produkci shodit aplikaci a zároveň se o tom s jistotou dozví.

A ještě jedna věc:

Pokud už Nette podporuje constructor injection a property injection nebylo by od věci rozšířit/pozměnit stávající chování inject metod a začít podporovat čistou setter injection označenou anotací (ať jsme konzistentní) a nikoliv slovem „inject“ v názvu. Direktiva setup nechť zůstane na „ostatní“ věci kromě injektáží.

Editoval pekelnik (29. 4. 2013 11:12)

Šaman
Člen | 2634
+
0
-

@pekelnik: Ve většině s tebou souhlasím, jen ten argument nemůžete mít všechno je irelevantní. Proč se teda pachtit s nějakou DI a teoretickou čistotou, když ji nemůžeme mít komplet?

<?php
private $cache;
public function __construct()
{
  $this->cache = ServiceManager::getCache();
}
?>

Nemůžu mít všechno, ale mám to krásně zapouzdřeno.

Jinak injector chápu jako setter, ale přeci jen v tom cítím rozdíl. Injector voláš typicky jen jednou ihned po vytvoření nové instance, settit můžeš častěji. Nevadí mi, že jsou tyto metody odlišeny i názvem. Ale je fakt, že většinou je lepší používat konstruktor.

pekelnik
Člen | 462
+
0
-

@Šaman doufám že „zapouzdřeno“ nepopisuje tu ukázku kódu… a tím „nemůžete mít všechno“ mám na mysli, že nemůžeš mít zároveň toto:

  • property injection
  • private property

Alespoň ne v čistém PHP. Dá se použít chování Nette\Object. Dá se upravit chování Nette\Object ale závislost je to nemilá… Tak asi tak…

To co nazýváš „injektorem“ je ve skutečnosti opravdu pouze setter. Další možností je write-once property v Nette\Object. Důležité je se při těchto úvahách soustředit na to, že „kontejnér neexistuje“ ;)

Editoval pekelnik (26. 4. 2013 17:37)

Šaman
Člen | 2634
+
0
-

Aha, já to myslel tak, že nemůžeme mít DI i zapouzdřeno :) Ten kód byl trošku provokačka, přiznávám.

Jinak o kontejneru nebyla řeč, jen mi nevadí mít některé settery pojmenované inject a vím, že pokud tuto metodu používám jindy než při vytváření nové instance, asi dělám něco špatně. Dále setter chápu trochu jako nepovinný parametr, zatímco injector bych nastavit měl (teda nenastavuji injector, ale jeho pomocí nastavuji property, ale snad si rozumíme).

pekelnik
Člen | 462
+
0
-

@Šaman mě na inject metodách sere toto:

  1. je to metoda navíc… setter většinou existuje také…
  2. přijde mi divné v kódu – když vytvářím objekt ručně – volat inject… když chci set…

ale to je OT…

enumag
Člen | 2118
+
0
-

@pekelnik: To by se dalo řešit takhle. Osobně ale závislosti které chci injectovat mám jako write-once, tzn. pouze inject* metoda a žádný getter natož setter.

Editoval enumag (26. 4. 2013 23:59)

Šaman
Člen | 2634
+
0
-

pekelnik napsal(a):

@Šaman mě na inject metodách sere toto:

  1. je to metoda navíc… setter většinou existuje také…
  2. přijde mi divné v kódu – když vytvářím objekt ručně – volat inject… když chci set…

ale to je OT…

Právě že inject* metody používám tam, kde klasický setter nechci. Nemám setDatabase(), setCache(), setFooRepository(), setAnotherService(). Tam, kde má smysl nastavovat parametry za běhu, tam patří setter, pokud je to něco co se má injectovat jen při vytváření objektu, tak inject. Zatím jsem nenarazil na sporné případy. Tam bych pk asi volil setter a injectování řešil ručně v configu.

David Grudl napsal(a):

Tohle už v Nette existuje, viz třeba tady https://github.com/…r/Method.php

Je někde příklad použití? Na čisté třídě dědící NObject mi to nefunguje.

Editoval Šaman (27. 4. 2013 11:59)

Filip Procházka
Moderator | 4668
+
0
-

@Šaman musíš takto vylepšit __call()

Majkl578 napsal(a):

Tahle diskuze nicméně není pouze o Presenterech, ale o DI obecně.

Pleteš se, tahle diskuze je jen a pouze o presenterech, protože všude jinde se standardně používá constructor injection a na tom se shodneme i s lidmi co používají v presenteru $this->context.

Editoval Filip Procházka (28. 4. 2013 17:16)

Majkl578
Moderator | 1364
+
0
-

Filip Procházka napsal(a):

Pleteš se, tahle diskuze je jen a pouze o presenterech, protože všude jinde se standardně používá constructor injection a na tom se shodneme i s lidmi co používají v presenteru $this->context.

Nikoliv, dostuduj si prosím realitu. Inject metody se volají na všech službách, ne pouze presenteru. Rovněž „problém“ přeplněného konstruktoru nesouvisí pouze s presenterem, ale s jakoukoliv službou. Zda je to standardní pouze pro presentery nebo ne je irelevantní otázka, vzhledem k tomu, že se to týká služeb globálně.