Co je služba a co není…
- nanuqcz
- Člen | 822
Ahoj, různé diskuze na tomto fóru mě přiměly k téhle otázce. Jsou nějaké obecné pravidla pro rozpoznání, jestli by daná třída měla být službou (Dibi, Model, RobotLoader), nebo neměla (Form apod.) – a tím pádem je chyba jí registrovat do DI kontejneru?
Nějaký rozdíl tam sice vnitřně „cítím“ :) ale pokud existují ucelené poučky.. Taky to moc nesouvisí s Nette, ale odpadne kvůli tomu plno zbytečných dotazů tady na fóru si myslím. Díky
Editoval xxxObiWan (14. 6. 2011 9:45)
- Filip Procházka
- Moderator | 4668
Statické třídy (třeba takový ActiveRecord) tam nepatří :) Ale obecná poučka mě nenapadá. Přijde mi, že se to dá odvodit „citem“. :)
Kdyby mě něco napadlo, tak se vrátím podělit se.
Editoval HosipLan (14. 6. 2011 10:08)
- _Martin_
- Generous Backer | 679
Ještě bych to rozšířil: kde je ta úroveň, na které se může použít
ono new
, resp. se vytváří Context? Je to jenom
bootstrap? A jak dostanu do presenteru nějakou svou službu? Je
v pořádku vytvořit v presenteru context s pevně zadanou cestou
ke configu?
Edit: je služba správné označení? Např. v článku na zdrojáku, kde se probíralo zatím čisté DI, nebyla o službách zmínka – pouze se závislosti dostaly vně třídu.
Editoval _Martin_ (14. 6. 2011 10:11)
- nanuqcz
- Člen | 822
_Martin_ napsal(a):
Edit: je služba správné označení? Např. v článku na zdrojáku, kde se probíralo zatím čisté DI, nebyla o službách zmínka – pouze se závislosti dostaly vně třídu.
V tom případě by ale měl mít DI Container i funkci vždy vytvořit novou instanci třídy
$instance1 = $container->myClass;
$instance2 = $container->myClass;
Takhle to ale Nette DI Container nedělá (pokud vím) a vrací pokaždé odkaz na jednu instanci (protože je určen jen pro služby?)
Editoval xxxObiWan (14. 6. 2011 10:37)
- Filip Procházka
- Moderator | 4668
xxxObiWan napsal(a):
Jsou nějaké obecné pravidla pro rozpoznání, jestli by daná třída měla být službou (Dibi, Model, RobotLoader), nebo neměla (Form apod.) – a tím pádem je chyba jí registrovat do DI kontejneru?
Logicky, služba se tváří jako singleton, jsou to třídy, které nejsou statické, ale není potřeba, aby existovaly více než jednou.
_Martin_ napsal(a):
Kde je ta úroveň, na které se může použít ono
new
, resp. se vytváří Context? Je to jenom bootstrap?
V bootstrap je potřeba psát nějaké new
? Na new
není nic špatného, ale je dáno, že když používáš DI, tak by ve
službě neměla být instanciace jiných služeb, závislosti by měla
dostat.
Třeba tohle je úplně v pořádku, v objektu je new
:
class Service extends Nette\Object
{
public function __construct(Nette\Caching\IStorage $storage)
{
$this->cache = new Nette\Caching\Cache($storage, 'Service');
}
}
Samozřejmě si pak třídu nakonfiguruješ, aby dostala
$storage
jaký ty chceš. Takže třeba
FileStorage
.
A jak dostanu do presenteru nějakou svou službu?
Do Presenteru? Nebo Containeru?
Do Presenteru patrně pomocí vlastní implementace Nette\Application\PresenterFactory. Měl jsem někde hotovou implementaci s autowiringem, ale blbě se to konfiguruje.
Do Containeru buď pomocí configu, nebo tím, že rozšíříš
Configurator a přidáš si tam vlastní createService*
Je v pořádku vytvořit v presenteru context s pevně zadanou cestou ke configu?
Proč bys to dělal? Pokud k tomu máš dobrý důvod, tak se dá ospravedlnit hodně věcí… Nepřijde mi to vyloženě jako prasárna, spíš jako zbytečnost.
`Yo dawn i heard you like Containers, so we put a container into your presenter in your application in your container, so you can inject dependecies, while you inject dependencies… `
Je služba správné označení? Např. v článku na zdrojáku, kde se probíralo zatím čisté DI, nebyla o službách zmínka – pouze se závislosti dostaly vně třídu.
Napadá tě lepší? Určitě se o službách zmíní až budou probírat Container :)
Editoval HosipLan (14. 6. 2011 10:41)
- Honza Marek
- Člen | 1664
HosipLan napsal(a):
Třeba tohle je úplně v pořádku, v objektu je
new
:class Service extends Nette\Object { public function __construct(Nette\Caching\IStorage $storage) { $this->cache = new Nette\Caching\Cache($storage, 'Service'); } }
Co je na tom v pořádku? Co ti brání si tam rovnou předat
Cache
?
- Filip Procházka
- Moderator | 4668
Nic mi nebrání, jenom mě nenapadl lepší příklad. Samozřejmě to není best practise. Je to otázka aktuální lenosti programátora to psát do configu, když to využiju jenom v jedné třídě, nebo nechci aby mi někdo měnil namespace :)
V komponentách se to třeba hodí. V těch komponentách co jsou v doplňcích k dispozici ostatním. Musíš uznat, že je výhodnější mít v požadavcích na konfiguraci své komponenty o službu méně (ten cache), než kdybys musel mít minimálně dvě služby. Je to zkrátka jednodušší a méně náchylné na chyby. A lepiči kódu jásají :)
Editoval HosipLan (14. 6. 2011 11:26)
- Honza Marek
- Člen | 1664
xxxObiWan napsal(a):
V tom případě by ale měl mít DI Container i funkci vždy vytvořit novou instanci třídy
To jde čistě vyřešit tak, že si jako službu zaregistruješ nějakou továrnu.
class NecoFactory
{
public function createNeco()
{
return new Neco;
}
}
- frosty22
- Člen | 373
Osobně v containeru využívám nejen služeb, ale mám v něm i metody, které vytváří vždy nové instance, dále i továrničky na formuláře, kvůli znovupoužitelnosti, atd. Díky tomu, si lze docela dobře udržet vzor DI a i u velkého projektu, na kterém nyní ve firmě pracujeme, lze dělat vše i velice přehledně v týmu, jelikož vytvoří-li například kolega nějaký model/objekt, tak v kontajneru ihned jsou vidět závislosti, všeho a tudíž je možné předejít kolizím, změním-li rozhraní jiného objektu (což by se dělat nemělo, ale občas není nazbyt)..
Dále díky @property-read už není problém v Netbeans s našeptáváním, čili celkově je pak contajner velice užiteční, například jak by mohla vypadat část kontajneru:
<?php
/**
* @property-read \Pdo $pdo
* @property-read \Model\Article $article
* @property-read \NotOrm $db
* @property-read \Translator $translator
*
*/
class Container extends \Nette\DI\Container {
protected function createServicePdo()
{
$db = $this->params['database'];
$dsn = ...;
$pdo = new \PDO($dsn, $db->username, $db->password);
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
$pdo->query('SET NAMES utf8');
return $pdo;
}
protected function createServiceDb()
{
return new \NotORM($this->pdo, new \NotORM_Structure_Convention('%s_id', '%s_id', '%s'));
}
/* Příklad: Továrnička na znovupoužitelný formulář přihlášení vracející nové instance */
public function createLoginForm(Presenter $presenter, $name)
{
$form = new Form;
$form->setTranslator($this->translator);
$loginForm = new LoginForm($form);
return $loginForm->getForm();
}
.........
}
?>
- Filip Procházka
- Moderator | 4668
Nechápu proč cpeš komponenty ke službám. By mě zajímalo, které prasátko to vymyslelo.
- arron
- Člen | 464
Abych se vratil k puvodni otazce (a totiz co je sluzba a co neni). Je to celkem zapeklite, a osobne na to neumim odpovedet (zatim). Nicmene ona hranice mezi tim, kdy je potreba pouzit DI a kdy uz nikoliv by se mozna dala definovat tak, ze pokud pouziju nejakou tridu na jednom jedinem miste, tak (IMHO) neni nutne ji pridavat do kontaineru. Ale kdyz ji pouziju ke stejnemu ucelu jinde („v jinem kontextu“), tak pak uz by mela byt vkladana pomoci DI. A samozrejme opodstatnena vyjimka potvrzuje pravidlo:-)
- Nox
- Člen | 378
Imho jde o to jestli daná třída má být zodpovědná za instanciaci dané třídy a jestli má a má mít informace k jejímu vytvoření. U Factory tříd (což je i ten Nette „kontejner“) je toto v pořádku, protože jejich účelem je právě tvorba objektů a znalost závislostí (resp. okruhu objektů a závislostí)
Presenter – komponenta/formulář je taky v pořádku, protože jak už naznačuje i namespace – formulář je jen UI prvek pro komunikaci s uživatelem, nemá mít sám žádnou funkcionalitu, má ji jen iniciovat, ale provedení už jen na někom jiném. A jelikož je Presenter zodpovědný za komunikaci s návštěvníkem, pak je to v pořádku.
Mimo takovýchto míst je ale převážně vhodné použít DI
- Tharos
- Člen | 1030
Nette\Utils\String
je úplně klasická utility class, vůbec se
z ní nevytváří instance.
- David Grudl
- Nette Core | 8227
Co je služba a co není?
Odpověď je strašně jednoduchá: služba se říká objektům, které se nacházejí v DI containeru (nebo service locatoru). Je to tedy jen věc názvosloví. Jakýkoliv instancovatelný objekt tak může být služba.