Stejné metody ve fasádách – jak na to?

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

Jak řešíte fasády, kdy máte stejný nějaký základ. Například skoro v každé takové fasádě mám findById, getById, getAll apod., které jsou stejné. Nabízí se logicky nějaká base třída, ale co jsem četl, je to spíše zneužití dědičnosti. Kompozice se nabízí taky, ale zase bude to o závislot navíc a už teď mám v některých fasádách dost závislostí.

Řešíte to nějak?

grogy
Člen | 147
+
0
-

Traity

Felix
Nette Core | 1189
+
+3
-

Nerekl bych ze je to zneuziti dedicnosti. Ba naopak se mi zda, ze tohle je ten spravny pripad.

Obecne mi traity prijdou spis jako zlo. Je jen opravdu malo pripadu, kdy chces defakto vicenasobnou dedicnost.

Alespon podle me. :)

Editoval Felix (15. 8. 2016 11:14)

Tharos
Člen | 1030
+
+2
-

Jiří Nápravník napsal(a):
co jsem četl, je to spíše zneužití dědičnosti

Bylo to v té literatuře podložené nějakými argumenty?

Mně v tomhle případě také přijde určité využití dědičnosti jako vhodné řešení. Fakticky se o specializaci jedná.

Učebnicovým (a IMHO nejčastějším) zneužitím dědičnosti je, když se někde využívá k pouhému sdílení kódu napříč nesouvisejícími třídami. Tam právě vztah specializace úplně chybí. Ale to není tenhle případ. :)

Editoval Tharos (15. 8. 2016 11:03)

Jiří Nápravník
Člen | 710
+
0
-

To nebylo v literatuře jako spíše ve fórech. Že zkrátka pokud člověk používá dědičnost, jen kvůli tomu, aby si usnadnil práci a nemusel psát stejný kód, je to špatně…

Mě to přijde v tomhle případě, jak usnadnění, tak určitá specializace. Na druhou stranu, že v těch klasických fasádách mám properties articleRepostiory/photoRepository apod. Teď bych musel mít nějakou mainRepository, abych mohl tyhle metody volat nad správnými repozitáři.

Tharos
Člen | 1030
+
+2
-

Jiří Nápravník napsal(a):

To nebylo v literatuře jako spíše ve fórech.

Pak bych se od toho možná úplně neodpichoval… :)

Že zkrátka pokud člověk používá dědičnost, jen kvůli tomu, aby si usnadnil práci a nemusel psát stejný kód, je to špatně…

Souhlas, je tam přesně to riziko, že se extends použije i v situacích, kdy nejde o specializaci.

Mě to přijde v tomhle případě, jak usnadnění, tak určitá specializace. Na druhou stranu, že v těch klasických fasádách mám properties articleRepostiory/photoRepository apod. Teď bych musel mít nějakou mainRepository, abych mohl tyhle metody volat nad správnými repozitáři.

Myslím, že se to celé dá poměrně uspokojivě vyřešit programováním proti rozhraní:

<?php
// abstract stuff


interface Entry
{

}


interface IRepository
{

	/**
	 * @param mixed $id
	 * @return Entry
	 */
	public function getById($id);

}


abstract class Facade
{

	/**
	 * @var IRepository
	 */
	protected $repository;


	/**
	 * @param mixed $id
	 * @return Entry
	 */
	public function getById($id)
	{
		return $this->repository->getById($id);
	}

}


// implementations


class Book implements Entry
{

}


class BookRepository implements IRepository
{

	/**
	 * @param int $id
	 * @return Book
	 */
	public function getById($id)
	{
		return new Book;
	}
}


/**
 * @method Book getById($id)
 */
class Books extends Facade
{

	/**
	 * @param BookRepository $bookRepository
	 */
	public function __construct(BookRepository $bookRepository)
	{
		$this->repository = $bookRepository;
	}

}
?>

Zajímavé je na tom řešení také to, že nepřijdeš auto-wiring.

Editoval Tharos (15. 8. 2016 12:29)

Jiří Nápravník
Člen | 710
+
0
-

Ok, tak tedy půjdu nejakou formou dedicnosti, diky za konkretni tip:)

fizzy
Backer | 49
+
0
-

V tom pripade fasada nema ziadny zmysel lebo len obaluje repository a nepridava do toho nic nove.. preco rovno nepouzit v zavislostiach priamo repository?

Tharos
Člen | 1030
+
0
-

@fizzy Samozřejmě existují případy, kdy fasády nepřidávají žádnou hodnotu a tedy nemá smysl se s nimi psát. Zrovna tak existují případy, kdy dávají velký smysl.

Kontextem téhle diskuze jsou takové případy, ve kterých smysl dávají. ;) Účelem té mé ukázky nebylo demonstrovat smyslupnost nějaké fasády, nýbrž demonstrovat práci s rozhraními v kombinaci s abstraktním předkem. Ten můj kód není ukázkou, která by se měla 1:1 objevit v nějaké aplikaci.

newPOPE
Člen | 648
+
0
-

@JiříNápravník nepises o fasadach ale o Repozitaroch. V zasade tam nemas moc co riesit. Ano tie metody sa opakuju. Mozes pouzit interface ako predpis, nasledne ho implementujes pomocou nejakej DbBaseRepository (predpokladam ze primarne ulozisko mas …Sql). A uz len dedis (ked sa ti nechce pisat). Akonahle budes potrebovat prepnut repository (napr. bude vyberat z Elasticu) staci ti napisat repository a implementovat tam dany interface. Sluzby ktore zavisia na danom rozhrani budu v pohode (to co spomina @tharos, programovanie proti rozhraniu).

Ja osobne ale ziadne BaseRepository nepouzivam ale to je zvyk…

Co sa tyka tych fasad tak tie obaluju prave domenovu (business) logiku (napr. Nejaka UserRegistrator::register ktora skontrluje ci user existuje, zaregistruje a vystreli even UserRegistered) a prave na to pouzivaju aj repositare. A nie je vobec problem sahat/pracovat priamo z repozitarom (vyzadovat ho ako zavislost) a zaroven aj s fasadou ak to potrebujes v klientskom kode tj. Controller, Command, …

GEpic
Člen | 562
+
0
-

newPOPE napsal(a):

@JiříNápravník nepises o fasadach ale o Repozitaroch. V zasade tam nemas moc co riesit. Ano tie metody sa opakuju. Mozes pouzit interface ako predpis, nasledne ho implementujes pomocou nejakej DbBaseRepository (predpokladam ze primarne ulozisko mas …Sql). A uz len dedis (ked sa ti nechce pisat). Akonahle budes potrebovat prepnut repository (napr. bude vyberat z Elasticu) staci ti napisat repository a implementovat tam dany interface. Sluzby ktore zavisia na danom rozhrani budu v pohode (to co spomina @tharos, programovanie proti rozhraniu).

Ja osobne ale ziadne BaseRepository nepouzivam ale to je zvyk…

Co sa tyka tych fasad tak tie obaluju prave domenovu (business) logiku (napr. Nejaka UserRegistrator::register ktora skontrluje ci user existuje, zaregistruje a vystreli even UserRegistered) a prave na to pouzivaju aj repositare. A nie je vobec problem sahat/pracovat priamo z repozitarom (vyzadovat ho ako zavislost) a zaroven aj s fasadou ak to potrebujes v klientskom kode tj. Controller, Command, …

Podle mě je dobré mít BaseRepository s předdefinovanými funkcemi, není pak totiž problém jednoduše přepnout Nette\Database → dibi a podobně anižby to ovlivnilo celkový chod projektu. Kde BaseRepository toto může umět ovládat.

Editoval GEpic (16. 8. 2016 9:18)

CZechBoY
Člen | 3608
+
0
-

@GEpic Pokud se ti tahá databáze celým projektem (jak to u Nette\Database bývá) tak to bude problém :-)

GEpic
Člen | 562
+
0
-

CZechBoY napsal(a):

@GEpic Pokud se ti tahá databáze celým projektem (jak to u Nette\Database bývá) tak to bude problém :-)

Databáze se nikdy mimo model nevolá, proč by to měl být problém? A co mi popřípadě doporučuješ? :)

newPOPE
Člen | 648
+
0
-

@GEpic nie som si vedomy ze by som tvrdil opak. Ked to tak vyhovuje nebranim nikomu. Zmena s Database na dibi a inde. To si moze dovolit len malokto. To ze sa pouziva len v modeli s tym suhlasim lenze aky je velky ten Model? 1, 2, 3 triedy? 40 tried? tam uz sa to potom meni narocnejsie. Prave preto by som ti doporucil pouzivat Repozitare kde si danu DB vrstvu zabalis. A ked ti nebude vyhovovat tak poprepisujes Repozitare (ano su sucastou modelu). A zaroven neskor mozes prist aj k pripadu, ze pobezis viac kniznic (Database, dibi, …) naraz ale opat ti to odtienia repozitare. No a ked budes este pisat aj nejake tie Entity od zaciatku (je to sice viac pisania) tak ti to ulahci aj konverziu resp. zapojenie Doctrine.

Jiří Nápravník
Člen | 710
+
+1
-

@newPOPE nepisu o repozitarich, ale o fasadach, tyhle metody byly spise jako priklady. Pouzivam Doctrine (kdyby). Jsou to takove ty metody nad repozitarem, uvnitr nich je ->repository->findOneby apod. Ja tam mam vice tech metod, slo mi o tyhle nektere spolecne metody, co jsou zkratka vsude.

Pouzivat vylozene repozitar primo v controlleru, to se mi nelibi, je to nizsi level, a mam to radeji v nejake fasade, pres kterou to volam. Pak kdyz resim nejake API k tomu, tak to mam na jednom miste.

Felix
Nette Core | 1189
+
0
-

Jiří Nápravník napsal(a):

@newPOPE nepisu o repozitarich, ale o fasadach, tyhle metody byly spise jako priklady. Pouzivam Doctrine (kdyby). Jsou to takove ty metody nad repozitarem, uvnitr nich je ->repository->findOneby apod. Ja tam mam vice tech metod, slo mi o tyhle nektere spolecne metody, co jsou zkratka vsude.

Pouzivat vylozene repozitar primo v controlleru, to se mi nelibi, je to nizsi level, a mam to radeji v nejake fasade, pres kterou to volam. Pak kdyz resim nejake API k tomu, tak to mam na jednom miste.

Tak to delas IMHO spravne, delam to tak take. Je dobre oddelovat logiku vrstev. Kdyz pak prechazis na jinou DB, pripadne uplne na jiny koncept, napr. SQL → noSQL, tak se to velmi hodi.

newPOPE
Člen | 648
+
0
-

@felix no a presne to je to co som spominal. O nacitanie Entity sa vzdy stara Repositar (getById, find($query), save, saveAll(array). Cize stale to mas na jednom mieste.

@JiříNápravník to ze si to obalujes je sice fajn ale nedava to zmysel nakolko tam fasada nic nerobi.

Proti gustu ziaden …

Jiří Nápravník
Člen | 710
+
0
-

@newPOPE ale ja v te fasade nemam jenom getby apod, ale vice veci… a pokud bych si predaval zavislost treba do controlleru jak repository (kvul getum) a pak treba kvuli tem dalsim vecem, tak mi to moc nedava smysl, mit dve zavislosti, kdyz mohu mit jen fasadu. a save a saveAll podle me nema v repozitari vubec co delat, to uz pak neni moc repository…

newPOPE
Člen | 648
+
0
-

@JiříNápravník asi citame roznu literaturu vid napr. http://martinfowler.com/…ository.html :)

pata.kusik111
Člen | 78
+
+2
-

@newPOPE @JiříNápravník Souhlasím s tím, že save a saveAll nemá v repository co dělat. Čtete stejnou literaturu, repository se má starat od čtení a Unit of Work o zápis.

newPOPE
Člen | 648
+
0
-

@JiříNápravník @pata.kusik111 asi sa nezhodneme. Unit of Work a repository patria do inych kategorii. Unit of Work riesi konzistenciu a konkurenciu. Repository spravu entit.

To ze to ma Kdyby implementovane tak ako ma je jedno z moznych rieseni. Bohuzial nic je ako to pozname „silver bullet“.

Jiří Nápravník
Člen | 710
+
0
-

Tak ono nejde jenom o to, jak to ma iplementovane Kdyby, ono je to v podstatě to samé v samotné Doctrine… Jejich defaultni EntityRepository taky nema zadne save/remove apod. A v docu te třídy mají „An EntityRepository serves as a repository for entities with generic as well as business specific methods for retrieving entities.“

Ono by to bylo celkem i problematické v repozitáři dávat třeba onen save. Vím sám, když jsem používal EntityDao v doctrine, pak jsem od toho rad upustil. U větších entit, tam byl problém s tím, že v tom save nemohl být flush() apod.

mkoubik
Člen | 728
+
+3
-

Bez ohledu na to jak tomu budeme říkat – jaká je přesně výhoda toho mít metody pro ukládání dat ve stejné třídě jako metody pro dotazování? Vždyť to jsou dva naprosto odlišné problémy a nedá se tam znovupoužít skoro nic.

duke
Člen | 650
+
+2
-

To mi připomíná ten vtip o tom, proč policajti vždycky jezdí ve dvojici. Jeden si prý pamatuje cestu tam, a druhý zpět. Také by se mohli hájit argumentem @mkoubik, že jde přeci o naprosto odlišné problémy. :-)