Preferovanie kompozicie pred base * triedou

Upozornění: Tohle vlákno je hodně staré.
duskohu
Člen | 769
+
0
-

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 | 6229
+
+1
-

Š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)

duskohu
Člen | 769
+
0
-

:-) @matej21 pekne, dakujem

enumag
Člen | 2129
+
0
-

@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)

David Matějka
Moderator | 6229
+
0
-

@enumag: diky, opraveno :)

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

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

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

Mna skor napadli 2 pripady ktore sa netykaju formularov ale komponent

1 Control registrovana DIC

priklad

  • 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

priklad

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

@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)

vvoody
Člen | 911
+
0
-

No v configu registruje interface do sekcie factories. Predpokladám že to je stratka pre implement. Tu by som skorej použil názov ITestControlFactory.

enumag
Člen | 2129
+
0
-

Aha, už to chápu. @duskohu tu první možnost má přesně tak jak jsem psal že bych to udělal, jen má špatně pojmenovaný ten interface. :-)