Stejné metody ve fasádách – jak na to?
- Jiří Nápravník
- Člen | 710
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?
- Tharos
- Člen | 1030
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
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
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)
- Tharos
- Člen | 1030
@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
@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 | 566
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)
- newPOPE
- Člen | 648
@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
@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 | 1245
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
@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
@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
@JiříNápravník asi citame roznu literaturu vid napr. http://martinfowler.com/…ository.html :)
- pata.kusik111
- Člen | 78
@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
@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
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.