Co je služba a co není…

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

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

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

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

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

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

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

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

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;
	}
}
Nox
Člen | 378
+
0
-

HonzaMarek akorát o tomto rozlišení (->service VS ->createService(…)) potom musí vědět i prvky co toto využívají pokud vim… což nevim jestli není problém…

frosty22
Člen | 373
+
0
-

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

Nechápu proč cpeš komponenty ke službám. By mě zajímalo, které prasátko to vymyslelo.

arron
Člen | 464
+
0
-

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

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

nanuqcz
Člen | 822
+
0
-

Nedávno jsme např. narazil na to, proč je Nette\Utils\Strings statická třída, a ne služba… Dokázal by někdo odpovědět?

Tharos
Člen | 1030
+
0
-

Nette\Utils\String je úplně klasická utility class, vůbec se z ní nevytváří instance.

David Grudl
Nette Core | 8227
+
+1
-

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.