Diskuze: Servicy on-demand

manwe
Člen | 44
+
0
-

Zdravim,

v kancelari resime u jednoho vetsiho projektu to, ze casto v komponentach/prezenterech automaticky (bezmyslenkovite) injectujeme z DI kontejneru ruzne servicy, protoze se obcas hodi (nejsou kriticke pro vykresleni komponenty, ale komponenta je treba ruzne casto vyuziva).

Uvedu priklad – komponenta EventDetail vykresluje detail udalosti. V udalosti je moznost zaslat hromadny e-mail vsem pozvanym ucastnikum. O zasilani e-mailu se stara servisa MailService. Realne se to poslani hromadneho e-mailu vyuzije tak pri 1 vykresleni kompoenenty z 1000, takze v 999 pripadech z 1000 se vytvari servica, ktera neni realne potreba.
V bezne situaci bych udelal neco jako

class EventDetail extends BaseControl{

	protected $mailService;
	protected $event;

	// mailService se vytvari v DI kontejneru pri vytvareni komponenty
	public function __construct(Event $event, MailService $service){
		$this->mailService = $service;
		$this->event = $event;
	}

	protected function sendMassEmail($subject, $text){
		$this->mailService->sendMassEventEmail($this->event, $subject, $text);
	}

}

Ted dochazime do situace, ze se zacina projekt znatelne zpomalovat, protoze DI kontejner musi casto vytvaret pomerne slozite servicy, ktere se casto nepouziva, jen proto, ze se „obcas“ v komponente/presenteru hodi.
Vedeme tedy diskuzi o tom, jak nejlepsim zpusobem servisy vytvaret realne „on-demand“ – jak to resite vy?

Aktualne se klonim k moznosti

class EventDetail extends BaseControl{

	protected $event;

	public function __construct(Event $event){
		$this->event = $event;
	}


	protected function sendMassEmail($subject, $text){
		// mailService se vytvari v DI kontejneru az pri realnem odesilani e-mailu
		$mailService = $this->getPresenter()->context->getByType(MailService ::class);
		$mailService->sendMassEventEmail($this->event, $subject, $text);
	}

}

(prime sahani do contextu z BasePresenteru bych jeste obalil nejakou metodou, ale princip zustava)

Jak to resite vy?
Diky za nazory.

Editoval raddy668 (5. 2. 2019 11:05)

David Matějka
Moderator | 6445
+
+2
-

protoze DI kontejner musi casto vytvaret pomerne slozite servicy

v čem je složité vytváření services? v inicializaci služby bys měl pouze přiřazovat vstupy z kontruktoru (a setup metod) do class properties

jinak nette má na to koncept accessoru, funguje to podobne jako generované tovarny, ale nevytváří to nové instance, ale vrací tu stejnou (z DI kontejneru). ale je to spíše workaround na špatné návrhy aplikace.

a taháním services z contextu se tak trochu vracíš do doby nette < 2.1, kdy DI nefunguvalo tak jak nyní a používal se právě pattern services locatoru

manwe
Člen | 44
+
0
-

Vim ze se servica nevytvari vickrat v DI kontejneru.

Vem si to, ze mas treba nejakou slozitejsi servicu, ktera vyuziva dalsich X servis (nerikam ze je to tak dobre, ale ne vsechno v projektu muzes ovlivnit), takze kdyz si ji nainjectuju do nejake komponenty takovou slozitou servisu a nevyuziju, tak treba instancuju 10 trid zbytecne (a ja nemuzu realne prochazet kazdou z tech servis a optimalizovat ji, aby v konstruktoru treba realne delala jenom to nejmene nutne veci). A tohle se treba deje 20–30× pri vykresleni nejake stranky v administraci.

To pak ten chod uz fakt zpomali…

David Matějka
Moderator | 6445
+
0
-

chápu, co chceš říct.

Pokud není v tvých silách optimalizovat vytváření těch závislostí, tak použij ten accessor. jde asi o nejmenší zlo – máš tak stále celkem jednoznačně definované závislosti, které předáváš přes DI, ale vytvářejí se lazy.

edit: ale i tak bych zkusil nejdřív spustit profiler nad aplikací a třeba zjistíš, že ta optimalizace není tak komplikovaná :)

CZechBoY
Člen | 3608
+
+7
-

Beztak se v nějakým modelu tahaj data z db nebo jinýho pomalýho zdroje a proto to ta trvá…
Vytvořit stovku objektů zas tak pomalý není – záleží o jakých jednotkách se tady bavíme.

duke
Člen | 650
+
+1
-

raddy668 napsal:

Vim ze se servica nevytvari vickrat v DI kontejneru.

To není to, co se ti snažil @DavidMatějka říct. Mluvil o konceptu automaticky generovaného accessoru v kontrastu s konceptem automaticky generované factory (který DI kontejner také podporuje, a který v případě factory vytváří vždy novou instanci).

raddy668 napsal:

Vem si to, ze mas treba nejakou slozitejsi servicu, ktera vyuziva dalsich X servis (nerikam ze je to tak dobre, ale ne vsechno v projektu muzes ovlivnit), takze kdyz si ji nainjectuju do nejake komponenty takovou slozitou servisu a nevyuziju, tak treba instancuju 10 trid zbytecne (a ja nemuzu realne prochazet kazdou z tech servis a optimalizovat ji, aby v konstruktoru treba realne delala jenom to nejmene nutne veci). A tohle se treba deje 20–30× pri vykresleni nejake stranky v administraci.

To pak ten chod uz fakt zpomali…

Pokud by to tak skutečně bylo, tak by přesně toto řešil ten accessor, protože by odložil instancování všech těch závislostí (resp. jen těch dosud jinde nepoužitých) složité servisy do doby, než bys na tomto accessoru zavolal metodu get.

manwe
Člen | 44
+
0
-

Aha, tak to jsem nepochopil, diky za nasmerovani, zkusim si o tom nekde neco najit.