Preferovanie kompozicie pred base * triedou
- duskohu
- Člen | 778
Jan Tvrdík: Přesouvám sem příspěvky z původního vlákna, odkud jsem je smazal.
Přesunuté příspěvky
Šaman
I do služeb to má své opodstatnění, i když jen ve specifických případech. Byl bych rád, kdyby ta možnost zůstala, nebo šla alespoň nějak explicitně zapnout. Já to používám u Base* tříd, když nechci zaplácat konstruktor takovými věcmi, které potřebuji vždy (u mě TemplateLocator). Druhá možnost by byla tahat to skryté z kontejneru, ale k tomu bych se nerad vracel…
matej21
@Šaman: lepsi je se vyhnout base* tridam a jejich dedeni kdykoliv je to mozny (a mozny to je prekvapive casto :) ). snaz se proste preferovat kompozici.
taky jsem mel nejakou BaseFormFactory, ktera vyzadovala pres inject metody translator, mapper apod. tu jsem zdedil a vytvoril ConcreteFormFactory..
tedka uz si v ConcreteFormFactory vyzadam BaseFormFactory v konstruktoru jako zavislost a vsechno krasne funguje
jinak ta moznost zustava a pravdepodobne i zustane, staci nastavit „inject“ na true u ty sluzby
Šaman
Aha, ok, to zapinání těch několika málo služeb je přijatelné.
Tu kompozici můžeš rozebrat? Pokud potřebuju načítat šablonu, tak to
řeším kompozicí – TemplateLocator je v jiné třídě, nebastlím to
v BaseControlu. Ale bude potřeba všude a BaseControl s ním umí automaticky
pracovat (stejně jako presenteru nemusím pokaždé ručně nastavovat
šablonu).
Původní příspěvek, kterým toto vlákno začínalo
Caute zaujala ma tato diskusia: Vydání Nette 2.1 do konce roku s maximálním zachováním BC, rad by som vedel ako to @matej21 myslel. Takze keby si mohol bol by som rad keby si to ako @Šaman pisal mohol rozobrat? Lebo aj mna to dost zaujalo.
Editoval Jan Tvrdík (21. 11. 2013 16:43)
- David Matějka
- Moderator | 6445
Šaman tam potom napsal druhej pripad, kde se base tridam vyhyba spatne –
controly (prvni pripad jsou presentery).
muj prispevek byl smerovany na obecnejsi sluzby (a tovarnicky), jako jsou
repository (o tom, jak je nededit psal Filip Procházka), nebo tovarnicky na formulare, na mail
message apod. ukazu na prikladu tovarny na form, jak jsem casem menil
postup.
1. mam obycejnou tovarnu na formular
class ConreteFormFactory
{
public function create()
{
$form = new Form;
//...
return $form;
}
}
2. najednou potrebuju formulari nastavit translator
class ConcreteFormFactory
{
protected $translator;
public function __construct(ITranslator $translator)
{
$this->translator = $translator;
}
public function create()
{
$form = new Form;
$form->setTranslator($this->translator);
return $form;
}
}
3. zjistim, ze ten translator potrebuju v kazde tovarnicce na form a nechce se mi se opakovat, tak vyvorim nejakou BaseFormFactory:
abstract class BaseFormFactory
{
protected $translator;
public function __construct(ITranslator $translator)
{
$this->translator = $translator;
}
public function create()
{
$form = new Form;
$form->setTranslator($this->translator);
return $form;
}
}
a ConreteFormFactory se krasne zmensi:
class ConreteFormFactory extends BaseFormFactory
{
public function create()
{
$form = parent::create();
return $form;
}
}
muzu si potom i hezky „globalne“ upravovat dalsi veci formularu, jako renderer atd.
4. a sakra, pro vytvoreni formulare potrebuju nejakou dalsi zavislost
class ConreteFormFactory extends BaseFormFactory
{
protected $someService;
public function __construct(SomeService $someService, ITranslator $translator)
{
$this->someService = $someService;
parent::__construct($translator);
}
public function create()
{
$form = parent::create();
return $form;
}
}
a uz bobtna konstruktor a hlavne si musim hlidat zavislosti BaseFormFactory a kdyz dojde ke zmene, tak upravit vsechny ConcreteFormFactory…
5. ok, je tu reseni.. inject metody!
abstract class BaseFormFactory
{
protected $translator;
public function injectTranslator(ITranslator $translator)
{
$this->translator = $translator;
}
public function injectRenderer()
{
....
}
public function injectMapper()
{
.....
}
public function create()
{
$form = new Form;
$form->setTranslator($this->translator);
return $form;
}
}
class ConreteFormFactory extends BaseFormFactory
{
protected $someService;
public function __construct(SomeService $someService)
{
$this->someService = $someService;
}
public function create()
{
$form = parent::create();
return $form;
}
}
6. vsechno krasne funguje, muzu pridavat zavislosti… ale… nejak se mi to nelibi.. ty inject metody by tam nemely co delat
class BaseFormFactory
{
public function __construct(ITranslator $translator, Renderer $renderer, Mapper $mapper)
{
$this->translator = $translator;
//...
}
public function create()
{
$form = new Form;
$form->setTranslator($this->translator);
//....
return $form;
}
}
class ConreteFormFactory
{
protected $someService;
protected $baseFormFactory;
public function __construct(SomeService $someService, BaseFormFactory $baseFormFactory)
{
$this->someService = $someService;
$this->baseFormFactory = $baseFormFactory;
}
public function create()
{
$form = $this->baseFormFactory->create();
return $form;
}
}
Editoval matej21 (21. 11. 2013 18:24)
- enumag
- Člen | 2118
@matej21:
Když v 6. předáváš BaseFormFactory v konstruktoru, nemá už smysl od ní dědit. Zřejmě jsi to při kopírování zapomněl odmazat. ;-)
S tvým návrhem souhlasím. Dříve jsem používal dědičnost ale poslední měsíc se snažím přepisovat na to co jsi uvedl.
Editoval enumag (21. 11. 2013 18:16)
- Jiří Nápravník
- Člen | 710
Díky za tip, určitě je to takhle příjemnější a lepší… Teď mám v aplikaci base-dědění v podstatě jen z BasePresenterů, ale tam se tomu moc vyhnout asi nejde, což:-)
- thunderbuff
- Člen | 164
@matej21: Velmi pěkný návrh! Jen přemýšlím ještě nad jednou mouchou. Představ si situaci, kdy potřebuješ data vytvářet i editovat. Pokud dědím Form hezky postaru, tak si vytvořím abstraktní form dědící od BaseForm. Tomu formu vytvořím všechny inputy mimo submitu a onValidate callback. Dál z něj dědím Create a Edit formuláře. Těm dvěma formulářům vytvořím jen submit tlačítko (jedno s popiskem „vytvoř“, druhý s popiskem „uprav“) a každému vlastní onSuccess callback.
Jak by se dal tento koncept přepsat do tvého návrhu? Řetězení factory už není moc elegantní a znamená to skoro tolik psaní, jako moje současné constructor hell. Nebo na to jdu totálně špatně?
- duskohu
- Člen | 778
Mna skor napadli 2 pripady ktore sa netykaju formularov ale komponent
1 Control registrovana DIC
- mame klasicku BaseControl ktora sluzi na to ze nastavuje sablobu vsetkym control
- potrebuje na to sluzbu ktora to vykonava
- mame TestControl ktora ma tiez nejaku zavyslost cez construct injection si ju vyziada
- ITestControl zaregistrujeme do configu
- injectneme do presentra a pouzivame
2 Control vytvorena cez instanciu v tovarnicke
- mame nejaku TestBControl ktora nema zavyslosti ktore by vedela zyskat z DI containera, tym padom ju ani nepotrebujem registrovat v neone
- ale kedze tato control chce vyuzivat automaticke nastavenie sablony ktore mu ponuka BaseControl tak ju tiez podedim
- lenze BaseControl si vyzaduje sluzbu pre nastavenie sabloby, tak mu ju poslem (podla mna tiez nie velmi ciste, alebo ano?)
- a znovu pouzivam
Tento navrh nie je podla mna „koser“ , ale zaujimalo by ma ako by sa toto dalo riesit pomocou hore spomenuteho riesenia. Dalsi pripad su formulare, tiez som sa stretol z riesenim BaseForm ktory je Control a vytvara sa v nom form aby sa dala rozsirit funkcionalita formu.
- enumag
- Člen | 2118
@duskohu: 1) se mi nelíbí protože komponenty imho nemají v DIC co dělat. 2) se mi nelíbí ze stejného důvodu jako tobě. Raději bych upravil 1) na generovanou továrnu ITestControlFactory která by vyráběla TestControl. Závislosti TestControl by DIC injectnul do vygenerované factory a ta později do komponenty. Nebo mi něco uniká a tohle použít nejde?
Editoval enumag (22. 11. 2013 16:37)