Dependency injection si razí cestu do 2
- Petr Daňa
- Člen | 109
Mě osobně se Context nelíbí. To už tu je Control, navíc dost často se v aplikacích používá „content“, to je samý „con“, už vidím ty překlepy způsobené používáním napovídání IDEčkama. Navíc jestli to jsou v podstatě jen služby použitelné v jiných objetech, proč to nepojmenovat jednoduše Services nebo tak nějak?
- Patrik Votoček
- Člen | 2221
Petr Daňa napsal(a):
…už vidím ty překlepy způsobené používáním napovídání IDEčkama…
K takovému překlepu naštěstí u namespace verze dojít nemůže.
Nette\Context
vs Nette\Application\Control
(další
bod pro namespace… :-p )
- v6ak
- Člen | 206
Proč by nemohlo? Nevím, jak jsou na tom PHP IDE s namespaces, ale třeba v Javě mi Eclipse nabízí třídy z různých packages (i když asi prioritně nabízí importované třídy apod.) a při zvolení mi přidá import (něco jako use), je-li potřeba, popřípadě doplní na fully-qualified verzi, nejde-li jinak. (V Javě nejsou aliasy, takže v případě používání dvou tříd se stejným názvem v jednom souboru musí být alespoň jedna fully-qualified.)
EDIT: malá editace
Editoval v6ak (18. 9. 2010 14:41)
- Honza Marek
- Člen | 1664
Půjde hodit parametrem konstruktoru služby nějaká jiná služba? (jak to předváděl Fabien na workshopu Symfony)
- Honza Marek
- Člen | 1664
Taky by tomu Contextu/ServiceContaineru/… slušel ArrayAccess.
Další věc je, že v komponentách a presenterech není metoda na získání kontextu, ale to je předpokládám dočasná záležitost.
- pekelnik
- Člen | 462
ArrayAccess by byl jistě přínosný – ovšem stejně tak by mohl
fungovat „ObjectAccess“.
Dále bych si tam dovedl představit nějaké aliasy jako bylo dřív
Environment::setServiceAlias()
protože:
<?php
$router = $this->context['Nette\Application\IRouter']; // je celkem úlet :)
$router = $this->context->Nette\Application\IRouter; // vůbec nende... narozdíl třeba od "defaultRouter"
?>
Naopak bych zrušil všechny ty gettery jako:
<?php
//Application...
function getRouter()
{
return $this->context->getService('Nette\Application\IRouter')
}
?>
Je to skutečně velmi zamlžující… člověk aby měl jednu továrnu na výrobu a jednu na získávání „služby“.
<?php
$router = $this->context->router; // je mnohem přátelštější a přímočařejší (a na funkčnosti to nic nemění)
?>
- paranoiq
- Člen | 392
Honza Marek napsal(a):
Další věc je, že v komponentách a presenterech není metoda na získání kontextu, ale to je předpokládám dočasná záležitost.
já předpokládám, že to je záměr. context by sis neměl sám brát, měl bys ho dostat. jakmile by sis třídou pro kontext někam šahal bylo by to porušení principu DI
Editoval paranoiq (29. 9. 2010 15:03)
- pekelnik
- Člen | 462
@martin: to co popisuješ se dělá takhle:
<?php
$context->addService('foo', function () {
return new FooService();
});
?>
@paranoiq: myslím že to bylo myšleno tak že v presenteru není ani zmínka o $contextu a já se přidávám k tomu „Zatím“ :)
Editoval pekelnik (29. 9. 2010 15:58)
- Honza Marek
- Člen | 1664
paranoiq napsal(a):
Honza Marek napsal(a):
Další věc je, že v komponentách a presenterech není metoda na získání kontextu, ale to je předpokládám dočasná záležitost.já předpokládám, že to je záměr. context by sis neměl sám brát, měl bys ho dostat. jakmile by sis třídou pro kontext někam šahal bylo by to porušení principu DI
A kudy bych ho měl dostat třeba do toho presenteru?
- Honza Marek
- Člen | 1664
Nechceš Davide použít v Nette Symfonní DI? Myslím, že Fabien má to kolo opravdu krásně kulaté.
- David Grudl
- Nette Core | 8227
Nemám s tím praktickou zkušenost a tedy netuším, jaké to má plusy a mínusy. Ale klidně zkus vytvořit prototyp a demonstrovat ho.
- Honza Marek
- Člen | 1664
David Grudl napsal(a):
Nemám s tím praktickou zkušenost a tedy netuším, jaké to má plusy a mínusy. Ale klidně zkus vytvořit prototyp a demonstrovat ho.
Prototyp je aplikace se zabudovanym symfonním DI? Uf… to bych musel přepsat celej Nette\Configurator a pár dalších míst v Nette.
Zkusím popsat, co se mi líbí:
- Symfonní DI container se nikde nevnucuje. Jen ví, jak má poskládat objekty. Třídy o něm nemají nejmenší tušení. V nette je na některých místech natvrdo zadrátován Nette\Context.
- Možnosti nastavení skládání objektů jsou poměrně univerzální. Dá se mu říct třída, parametry konstruktoru (což mohou být i jiné služby), zavolání dalších metod i s parametrama (např. setterů). Nastavování statických proměnných, co v Nette občas jsou, to teda neumí.
- Implementace služby jde velmi snadno vyměnit. Například můžu mít jednoduchou univerzální třídu pro servisní vrstvu modelu, která dostane parametrem název entity o kterou se má starat. Když to přestane stačit, vyrobím novou třídu a přepíšu jen konfigurák.
- Třídy dostanou jen to, co potřebují. Ne celý Context. Z toho vyplývá i snadná testovatelnost.
- Parametry (obdoba environment variables) to umí. Všechny ty tempdiry by šly nastavit jako parametr.
- David Grudl
- Nette Core | 8227
Jak by se konkrétně řešit ten FileStorage, kterému je potřeba předat journal? Přičemž journal je potřeba jen v případě, že využiju tagy, v ostatních případech ho nemá smysl inicializovat.
- Honza Marek
- Člen | 1664
Je pravda, že tady mě nenapadá lepší řešení než tam hodit nějaký Context/Container. Snad je někdo chytřejší než já :) Nicméně alespoň by konstruktor měl brát interface a ne přímo Nette\Context.
- mkoubik
- Člen | 728
Mám hodně odvážnej návrh, co takhle dát do Nette\Object
(nebo nějakého potomka – to si můžu implementovat sám)
setContenx(Nette\IContext $context)
, aby se tím zbytečně
neza*íraly konstruktory těch tříd, kde někdo uznal, že v nich bude
Context potřeba.
S testovánim není problém:
$ctx = new Context();
$log = new MockLogger()
$ctx->addService('Logger', $log);
$model = new FooModel();
$model->setContext($ctx);
assert...
Raději bych ale viděl klasické předávání přímo objektů setterem/konstruktorem (s oželením továrniček) a „wiring“ třeba v anotacích nebo NEONu, případně možnost použvat oba přístupy (je to vůbec možné?)
- Honza Marek
- Člen | 1664
mkoubik napsal(a):
Mám hodně odvážnej návrh, co takhle dát do
Nette\Object
(nebo nějakého potomka – to si můžu implementovat sám)setContenx(Nette\IContext $context)
, aby se tím zbytečně neza*íraly konstruktory těch tříd, kde někdo uznal, že v nich bude Context potřeba.
ne
- v6ak
- Člen | 206
Souhlas, je to celkem odvážná myšlenka, aspoň pro Nette\Object. Já jdu
v tomto směru opačnou cestou a nedávno mě napadlo, zda je vhodné, aby
java.lang.Object měl metody equals(Object) a hashCode() – zda by nebylo
lepší mít rozhraní Equalable.
Ale zpět k PHP, Nette a tomu návrhu: hlavní proti bych měl v tom
setteru:
- Mám u tříd rád určitou neměnnost, pokud to nějak rozumně jde. Co je jednou přiřazeno v konstruktoru, není již dále měněno.
- Ono může být trošku problematické, když mi někdo najednou změní kontext.
- mkoubik
- Člen | 728
Ono nemusí jít nutně o setter, může to být i konstruktorem, rozdíl je v tom, že se předávají přímo konkrétní objekty a ne jakýsi kontejner, takže třída nemusí o automatickém DI vůbec vědět. Mohlo by to například vypadat takhle:
class Model extends Object
{
private $logger;
public function __construct(ILogger $logger) {
$this->logger = $logger;
}
public function doSomething() {
$this->logger->logSomething();
...
}
}
v testu můžeš použít přímo new
a předat
ILogger
konstruktorem/setterem, ale klasické IoC kontejnery
fungují tak, že z nějaké konfigurace/anotací načtou strom závislostí a
když požádáš o Model
, tak podle toho vytvoří
instanci/použijí existující a předají mu konkrétní implementace všech
jeho závislostí.
Výhodou je variabilita – můžou nastat např. tyto situace:
- Všechny objekty používající
ILogger
používají stejnou instanci (singleton + jen jedna implementace) - Objekty používající konkrétní implementaci
DatabaseLogger
používají stejnou instanci (singleton) - Každý objekt závislý na
DatabaseLogger
má vlastní instanci - Existují např. 3 různé instance
DatabaseLogger
u lišící se závislostmi/nastavením a každá je používána určitou skupinou objektů
to už závisí na kontejneru a konfiguraci, přitom samotné business třídy vypadají jakoby se nechumelilo (až např. na anotace) a skládal si je programátor ručně.
Existuje i tzv. autowiring, kdy nic nenastavuješ a když požádáš o
IModel
, tak se kontejner pokusí najít třídu, která ho
implementuje, vytvoří její instanci, předá jí podobným způsobem
implementaci ILogger
atd, ale to už je docela magie.
Doporučuju nastudovat Spring Framework, nebo třeba JavaBeans.
Ještě dodám že chápu výhody stávájícího řešení (pomocí
__get($name)
v contextu se dají závislosti vytvářet
„líně“ v továrničkách), třídy frameworku to klidně můžou
používat, ale pro vlastní třídy (především modelovou vrstvu) by se
nějaké „true IoC“ hodilo. Mimochodem Nette\Context
by se mohl
taky předávat tímto způsobem, když by to tam už bylo.
Editoval mkoubik (6. 10. 2010 16:55)
- VasekPurchart
- Člen | 20
mkoubik napsal(a):
No IoC tím způsobem, že předáváš konstruktorem konkrétní objekty na základě nějakého interface a nějaká třída ti je spravuje – vrací ti instance na základě konfigurace, tak tak je to implementované právě v Symfony, o kterém se tu zmiňoval Honza Marek. Konfigurace se tuším píší externě do yamlu, ale je možný, že to jde i jinak.
- phx
- Člen | 651
Me se to libi. Z RobotLoader by slo vytahnout kdo implementuje jake rozhrani a zadna konfigurace by nebyla nutna. Jen v pripade, ze by bylo vic moznosti tak by to hodilo vyjimku a pozadovalo konfiguraci. Jinak by to mohlo fungovat „magicky“.
Jediny problem vidim v tom, ze vsechny objekty by se museli vytvorit pred predanim do konstruktoru. Coz by slo obejit predanim tovaren a definovat pozadovane interface napr pomoci anotace. Skoro by se mozna vyplatilo predat rovnou ten Nette\Context, ale osobne bych rad, aby objekt vubec nemusel byt na Nette\Context vazany a ani o nem nevedel.
Schudnou cestou by bylo aby samotne vytvoreni objektu bylo co nejmene narocne a kazdy objekt si resil sam svoje lazy loading. Tusim ze tu David zminoval napr inicializaci zurnalu. Hold o lazy inicializaci by se musel postarat sam objekt Jurnal.
- westrem
- Člen | 398
Tak vlakno som precital parkrat a kontrolujem ho denne na nove prispevky, no stale mam par nezrovnalosti. Taktiez som si musel objasnit pojem dependency injection intuitivne som vedel o co go, ale chcel som aj trochu teoreticky BG.
Z uvedeneho hlasujem pre context, aj ked je to v podstate uz asi zbytocne.
Skor by ma zaujimalo:
- ako bude rozlisitelne, ktore veci dany objekt potrebuje a teda co sa ma injektovat v kontexte? Alebo do kazdeho objektu predavat kompletny kontext?? Nebude to mat vplyv na vykon?
- rozmanitost injektovanych sluzieb je samozrejma obmedzovana len kreativou programatorov, ako sa dosiahne nejake rozumne inicializovanie sluzieb a ich nastavovanie?
- bude k tomu v konecnom dosledku dostupna poriadna dokumentacia, priklady? Podla mna sa jedna o celkom velky posun vo filozofii ako pisat veci v Nette (David sice pise, ze Nette obdobne funguje v podstate od zaciatku ale zaroven spomina, ze sa vzdy preferoval iny pristup k sluzbam) a preto by si zasluzil aj poriadne intro.
- Patrik Votoček
- Člen | 2221
westrem napsal(a):
- ako bude rozlisitelne, ktore veci dany objekt potrebuje a teda co sa ma injektovat v kontexte? Alebo do kazdeho objektu predavat kompletny kontext?? Nebude to mat vplyv na vykon?
Z principu věci ne. Protože objekty jsou v PHP standardně předávány jako reference. Tj. jediné co „zaberou“ je přidání dalšího ukazatele do paměti což je nula nula nic.
- bude k tomu v konecnom dosledku dostupna poriadna dokumentacia, priklady? Podla mna sa jedna o celkom velky posun vo filozofii ako pisat veci v Nette (David sice pise, ze Nette obdobne funguje v podstate od zaciatku ale zaroven spomina, ze sa vzdy preferoval iny pristup k sluzbam) a preto by si zasluzil aj poriadne intro.
Bude! Ostatně jako k celému Nette 2.0
- westrem
- Člen | 398
vrtak-cz napsal
Z principu věci ne. Protože objekty jsou v PHP standardně předávány jako reference. Tj. jediné co „zaberou“ je přidání dalšího ukazatele do paměti což je nula nula nic
To, ze sa objekty predavaju ako referencia samozrejme viem a z tohto som si vedomy, ze su to len ukazatele a ted ako sam pises nula nula nic, avsak na fore sa riesilo aj nieco o klonovani kontextu, co uz ak sa nemylim znamena vytvaranie duplikatu v pameti a ak ten kontext obsahuje zlozite objekty, ktore zaberaju vela miesta ich klonovanim sa teda vykon asi nezlepsi ba naopak. Alebo som zle pochopil princip? Za objasnenie dakujem
Bude! Ostatně jako k celému Nette 2.0
A srdecne sa na nu tesim ^^
- Honza Marek
- Člen | 1664
Vyrobil jsem hračku, která umožní definovat služby a jejich závislosti v konfiguračním souboru ve formátu neon. https://gist.github.com/623720
- Honza Marek
- Člen | 1664
Slidy Fabiena Potenciera o dependency injection: http://www.slideshare.net/…nzendcon2010
- bojovyletoun
- Člen | 667
Trochu mi to přijde jako krok zpět :)
viz přednáška „Jaro 2008:http://www.avc-cvut.cz/avc.php?… 18minut
slide
Nový název pro "controller“
Příliš mnoho con…
- Control
- Controller
- FrontController
- Container, COmponent, COnfig, Collection, COrnflakes
- Filip Procházka
- Moderator | 4668
Třídy začínající na „co“:
Nette\Component
Nette\ComponentContainer
Nette\Configurator
Nette\Context
Nette\Config\Config
Nette\Config\ConfigAdapterIni
je jich „jenom 6“ s tím že první dvě prakticky skoro nepoužiješ,
Configurator
si maximálně jednou rozsíříš a
Config
taky prakticky nepotřebuješ. Zbývá ti
Context
, který rozlišíš napsáním Cont
.
Ono né vždycky co ti příjde aktuálně cool, je tak cool, jako když to pak skutečně použiješ, nebo po roce používání.