Dependency Injection ve 2.1: sloučení factories a services
- David Grudl
- Nette Core | 8227
(Navazuji na starší příspěvek)
V současnosti lze definovat v DIC dva typy entit: služby a továrničky.
Rozdíl je v tom, jestli se instance služby uchovává a je dostupná přes
metodu getService
, nebo se pokaždé vytvoří nová při volání
metody create...
. Továrničky tak mohou mít navíc i parametry.
A compiler umí také vytvářet implementace továrniček definovaných
rozhraním.
Uvažuji nad tím, že bych rozdíl mezi obojím zrušil. Vše by se
definovalo v jedné sekci services
, takže nebude potřeba
přemýšlet nad tím, jestli jde o službu nebo továrnu. Rozdíl by
spočíval pouze na uživateli, jestli by k entitě přistupoval přes
getService()
nebo createService()
.
Narazil jsem na jediný problém, a to je nejasnost v konfiguraci při odkazování se na jinou službu:
services:
a: MyClass
b: OtherClass(@a)
Pokud se služba @a
nacházela v sekci factories
,
znamenal odkaz @a
zavolání této továrny a do konstruktoru
OtherClass
se předala nově vytvořená instance
MyClass
.
Pokud sekce sloučíme, @a
bude znamenat klasický odkaz na
sdílenou instanci služby a tedy i BC break. Způsob, jak docílit vytvoření
nové instance, je
krkolomnější: @container::createService(a)
- Filip Procházka
- Moderator | 4668
Moc to nechápu… Bavíme se zde pouze o sloučení sekcí? „implement“ by fungovalo dál?
Btw, již teď používám
services:
articles:
class: App\Articles(@doctrine.dao(App\Article))
factories:
doctrine.dao:
class: Kdyby\Doctrine\EntityDao
create: @entityManager::getRepository(%entityName%)
parameters: [entityName]
Napadá mě tedy, pokud chci předat novou instanci služby (pokud by se sloučily ty sekce), tak by se to dalo napsat takto
services:
a: MyClass
b: OtherClass(@a()) # nová instance
c: AnotherClass(@a) # sdílená instance
Ale @container::createA()
bych asi taky přežil :)
- David Grudl
- Nette Core | 8227
Honza Marek napsal(a):
To mi zní jako zrušení factories bez náhrady. Jsem pro.
Factories se pochopitelně zachová, přesněji rozšíří ;)
Filip Procházka napsal(a):
Napadá mě tedy, pokud chci předat novou instanci služby (pokud by se sloučily ty sekce), tak by se to dalo napsat takto
Jde skutečně jen o sloučení sekcí.
Pokud má služba @a
parametry, pochopitelně by se volala jako
továrna. Problém je, pokud parametry nemá. Pak je při současné
implementaci velmi komplikované rozlišit mezi @a
a
@a()
.
- Šaman
- Člen | 2662
Jak tohle teď vypadá?
Oproti 2.10 mi v @dev nefunguje
$container->createFoo();
, pokud mám foo
v sekci
factories
.
Našel jsem tohle vlákno, přesunul továrny do sekce services
a začal je vytvářet pomocí $container->createService('foo')
a to mi zase nefunguje, protože všechny komponenty potřebují v konstruktoru
nějaký kontejner a ono se jim to snaží vnutit nějaký z configu. A těch
je tam spousta.
Nakonec jsem nevím proč zkusil přesunout definici továren zpět do sekce
factories
a vytvářet je pomocí createService()
a
vypadá to, že to funguje.. ale je to dost WTF. Je to už finální stav, nebo
se to ještě bude měnit?
- Filip Procházka
- Moderator | 4668
Pokud někde vytváříš potomky
Nette\ComponentModel\Component
, tak bych důrazně doporučoval
překrýt konstruktor, aby nesomroval ty defaultní parametry (parent
a name).
- Šaman
- Člen | 2662
No, komponenty mám v configu a v továrničce je sosám pomocí
$container->createService($name)
. Co myslíš tím překrýt
konstruktor? Vždyť v konstruktoru komponenty přece nemám k dispozici
parenta, ne? (pokud si ho nenechám předat parametrem). A $name vlastně
taky ne.
Editoval Šaman (4. 6. 2013 5:04)
- Filip Procházka
- Moderator | 4668
Myslím tím prosté
public function __construct()
{
parent::__construct(); // ha, už o nich DIC neví
}
- Honza Marek
- Člen | 1664
Nutí někdo autowiring, aby cpal služby i do nepovinných parametrů? Podle mě by to neměl dělat.
- Filip Procházka
- Moderator | 4668
vojtech.dobes napsal(a):
Ústupek autowiringu, tak říkajíc.
Především plus pro api třídy. V konstruktoru tohle imho stejně nemá co dělat. Ale to už jsme OT :)
- Šaman
- Člen | 2662
Filip Procházka napsal(a):
Myslím tím prosté
public function __construct() { parent::__construct(); // ha, už o nich DIC neví }
Díky, tohle v BaseControlu řeší problém.
Chápu to tedy tak, že i když sekce factories
asi z důvodu
zpětné kompatibility funguje postaru, tak doporučované je mít všechno v
services
a služby od továren rozlišovat až při použití –
getService() vs. createService()?