Dependency injection si razí cestu do 2

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Petr Daňa
Člen | 109
+
0
-

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?

Ani
Člen | 226
+
0
-

Takže jestli to chápu, tak je to vlastně ContextServiceContainer. Kontejner se službama v kontextu k danýmu objektu? Zatímco jen ServiceContainer by byl jakoby „globální“?

Patrik Votoček
Člen | 2221
+
0
-

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
+
0
-

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
+
0
-

Netbeans pro PHP to taky tak dělá, vrták blábolí.

David Grudl
Nette Core | 8082
+
0
-

Tohle je spís argument proti Container, ne?

hrach
Člen | 1834
+
0
-

řekl bych, že ani pro jedno. vše začíná na cont* – a až následující písmeno je jiné.

Honza Marek
Člen | 1664
+
0
-

Půjde hodit parametrem konstruktoru služby nějaká jiná služba? (jak to předváděl Fabien na workshopu Symfony)

paranoiq
Člen | 392
+
0
-

… až budou fungovat parametry konstruktoru. také bych to tam rád viděl

Honza Marek
Člen | 1664
+
0
-

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
+
0
-

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í)
?>
_Martin_
Generous Backer | 679
+
0
-

@pekelnik Nebo by ty gettery mohly fungovat podobně, jako princip továrniček v komponentách (kdyby při volání getRouter() ještě router neexistoval, automaticky by se zavolala createServiceRouter()). Nebo je to moc velká magie?

paranoiq
Člen | 392
+
0
-

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
+
0
-

@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)

_Martin_
Generous Backer | 679
+
0
-

@pekelnik: Jasný – to už je jedno, zda na to je továrnička či callback – šlo o ten princip, že by se při použití getteru zavolala – v případě potřeby – automaticky sama. A nebo jsem špatně pochopil tvůj první příspěvek?

Editoval _Martin_ (29. 9. 2010 16:27)

Honza Marek
Člen | 1664
+
0
-

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?

pekelnik
Člen | 462
+
0
-

@honza: Měla by hotam injektovat aplikace při vytváření presenteru :)

Editoval pekelnik (29. 9. 2010 16:46)

Honza Marek
Člen | 1664
+
0
-

Jo a nedělá to. O to mi jde.

Honza Marek
Člen | 1664
+
0
-

Nechceš Davide použít v Nette Symfonní DI? Myslím, že Fabien má to kolo opravdu krásně kulaté.

David Grudl
Nette Core | 8082
+
0
-

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
+
0
-

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.
grey
Člen | 94
+
0
-

jinak tady je pěkně popsaná ta dependency injection ze symfony… taky bych byl pro, aby se použila…

David Grudl
Nette Core | 8082
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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:

  1. 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.
  2. Ono může být trošku problematické, když mi někdo najednou změní kontext.
mkoubik
Člen | 728
+
0
-

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 DatabaseLoggeru 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
+
0
-

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
+
0
-

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.

v6ak
Člen | 206
+
0
-

Bacha na ty továrny. Předám dvoum třídám stejnou továrnu a každá si z toho vyrobí svou instanci. (Pokud není v té továrně kód pro cacheování, což by znamenalo duplicity.) To asi není dobře, že?

Ad Spring: Orientačně vím. Prý je Google Guice lepší, ale neznám to tak podrobně.

phx
Člen | 651
+
0
-

Tovarnou jsem myslel neco co vrati existujici tridu nebo vytvori novou.

Napr:

function factoryXXX() {
	static $ins;
	if (!$ins) $ins = new XXX();
	return $ins;
}
pekelnik
Člen | 462
+
0
-

Honza Marek napsal(a):

Nechceš Davide použít v Nette Symfonní DI? Myslím, že Fabien má to kolo opravdu krásně kulaté.

Co tam takhle dát ještě ze zendu autoloader a pojmenovat to Nene! :D

westrem
Člen | 398
+
0
-

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.
univerz
Člen | 7
+
0
-

westrem napsal(a):
Nebude to mat vplyv na vykon?

+1. uz teraz mi pride, ze pamat a milisekundy na holej stranke s nette su trochu privela.

Patrik Votoček
Člen | 2221
+
0
-

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
+
0
-

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 ^^

v6ak
Člen | 206
+
0
-

Pokud si dobře vzpomínám, tak byla řeč o klonování Contextu. A to IMHO nebude až tak časté, aby to mělo významný vliv.

Honza Marek
Člen | 1664
+
0
-

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

jiriknesl
Člen | 56
+
0
-

Brrrrr, Context je imho stejně špatný, jako Manager.

Jedná se o kontext čeho? InjectionContext? Proč ne třeba Injector. Líbí se mi i Provider a Container. Evokuje to ve mě, že mi něco předá, že pro mě má nějaká data. Kdežto kontext si prostě neumím dát do kontextu. :)

Honza Marek
Člen | 1664
+
0
-

Slidy Fabiena Potenciera o dependency injection: http://www.slideshare.net/…nzendcon2010

bojovyletoun
Člen | 667
+
0
-

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
+
0
-

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í.