Obsluha událostí / presenter jako závislost

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

Ahojte, možná se to už někde řešilo, ale opravdu jsem se snažil hledat a nic. V naší aplikaci implementujeme návrhový vzor observer (konkrétně pomocí kdyby/events). Na příkladě zkusím popsat problém.

V aplikaci mám adresář „extensions“, kam se nahrávají rozšíření systému. V případě, že se v tomto adresáři objeví nové rozšíření, aplikace jej uživateli v administraci nabídne k instalaci. Existuje tedy nějaký objekt, který prochází adresář „extensions“, jeho obsah srovnává z nainstalovanými moduly v databázi a najde-li nějakou „novinku“ vyvolá událost, kde jako parametry této události odešle seznam těchto „novinek“. Potřebuji tedy zaregistrovat posluchače. Nyní dochází k problému. Já to potřebuji dát vědět uživateli, tzn. vidím dvě možnosti:

  1. Presenter bude přímo posluchač (to se mi líbí víc, než bod 2)
  2. Vytvořím posluchač, kterému předám presenter přes DI

V obou případech pak můžu obsloužit událost a vypsat flash message. Problém ale je, že nevím, jak získat presenter. Jelikož používám kdyby/events, musím posluchači přes config přidat tag kdyby.subscriber. V případě č. 1) musím tedy vytvořit presenter jako službu a předat mu „tags:“. To ale bohužel vytvoří úplně novou instanci presenteru, takže flash message nedostanu na výstup. A v případě 2) si musím v posluchači přes DI dodat presenter jako závislost. To ale nejde, protože když si dám do konstruktoru konkrétní presenter, tak mi tracy zahlásí, že ta služba neexistuje. Když si tam předám DI\Container a přes něj zavolám getService(), tak opět dostanu úplně jinou instanci, tak jako v bodě 1).

Prostě nějak nevím, jak to řešit. Poradíte prosím? Dík.

David Matějka
Moderator | 6445
+
+1
-

Nejdriv: snazit se ziskat presenter jako zavislost je (vetsinou) spatne.

Ale myslim, ze se to cele snazis resit moc komplikovane, proste si v presenteru (pripadne v komponente) pozadej tu sluzbu, aby ti vratila zmeny.

btw,

návrhový vzor observer (konkrétně pomocí kdyby/events)

v tomhle pripade jde spise o mediator

svezij
Člen | 69
+
0
-

Díky, nj, tento návrhový vzor jsem neznal a mnou popsaný případ se tak asi jeví. Nicméně, ať už je to mediator nebo observer, implementujeme to s kolegou pomocí obsluhy událostí. Snažíme se tvořit „modulární“ systém, který bude implementovat komunikaci pomocí událostí.

Možná to není úplně ideální případ, ale chtěl bych se držet mnou uvedeného příkladu v dotazu. Dejme tomu, že vytvořím rozšíření, nazvu jej např. Foo. Foo bude kontrolovat, zda aktuální uživatel, který nemá práva instalovat nová rozšíření, náhodou nevlezl do admina, kde je nové rozšíření připraveno. V takovém případě Foo pošle někomu e-mail o tom, že je potřeba rozšíření nainstalovat. Mně přijde mnohem elegantnější, aby Foo pouze naslouchalo systému a odchytilo si událost, která říká, že byla nalezena nová rozšíření, než si do Foo přes DI vložit jako závislost objekt, který tuto kontrolu dělá a přímo se na to dotazovat přes metodu. Stejně v systému „nutím“ každé nové rozšíření dědit od objektu „Extension“, tzn. že každé rozšíření už může s manažerem událostí komunikovat od prvopočátku.

Nebo myslíš, že komunikaci událostmi je vhodné používat jen při interakci s uživatelem? Jako události typu klik atd.? Tzn. pouze na úrovni presenterů? Jsem z toho trochu zmatený. Nyní v práci tvoříme hodně velkou apku, kde je nutné, abychom dělali věci po co nejmenších „nezávislých“ modulech. Je také nutné, aby ty moduly mezi sebou komunikovaly. Když jsme aplikaci vymýšleli, tak nám přišlo ideální si prostě jako závislost předávat manažer událostí a reagovat na požadované. Nejsem úplný začátečník, ale neznám vývoj takhle velkých aplikací, a přestože ze školy vím, jak které principy fungují, někdy je těžké je použít korektně. Nicméně, tak to chodí, a právě život mě to učí.

Jak tedy řešit situace, kdy se v systému něco stane, a já o tom chci dát vědět uživateli? Když se např. vytvoří požadované FTP spojení, tak přes presenter vypsat uživateli, že spojení bylo navázáno? Taky mám přece nějaký systémový objekt, který to spojení navazuje. Jo, asi budu spojení navazovat právě v presenteru, ale co když to potřebuji odchytit i v nějakém loggeru a vypsat uživateli, že to bylo zalogováno?

Díky moc.

David Matějka
Moderator | 6445
+
0
-

Nebo myslíš, že komunikaci událostmi je vhodné používat jen při interakci s uživatelem? Jako události typu klik atd.? Tzn. pouze na úrovni presenterů?

nn, jen myslim, ze na udalosti vyvolane v modelove vrstve by nemel primo reagovat presenter.

V takovém případě Foo pošle někomu e-mail o tom, že je potřeba rozšíření nainstalovat.

I tady je to asi ok. Jediny problem vidim prave v tom, ze presenter by mel na zaklade nejake kontroly naslouchat na udalost a zobrazit flash message. Kdy by se ta kontrola vyvolala? Jak by se na ni presenter navazal? je totiz problem, ze instance presenteru se vytvari tady, tudiz az tam by se mela vyvolat ta kontrola, ze bylo nainstalovano rozsireni (a nebylo by mozne ji vyvolat napriklad hned po startu aplikace). Napada me to resit treba takto:

  1. presenter si rucne zavola tu kontrolu, ze existuje nove rozsireni. Sluzba, ktera to kontroluje, by vyvolava udalost. Na to by pak mohlo reagovat rozsireni Foo a presenter by pak (ne)zobrazil upozorneni uzivateli
  2. udelat nejakou sluzbu (napriklad NotificationStorage) a potom nejaky listener (treba ExtensionInstalledListener), ktery by plnil to NotificationStorage a presenter by si odsud ty notifikace ziskal.

ale co když to potřebuji odchytit i v nějakém loggeru a vypsat uživateli, že to bylo zalogováno?

tady je uplne v poradku pouzit udalosti

svezij
Člen | 69
+
0
-

Hmm… zajímavé, vyzkouším naimplementovat a dám vědět (ale až po Velikonocích ;-) ). Ještě jednou dík moc, kdyby náhodou, ještě se ozvu. Hezké Velikonoce :-)

svezij
Člen | 69
+
0
-

Dobré ráno,

tak nakonec mám řešení, které je pro naši aplikaci ideální. Ad 1) by rozhodně fungovalo a implementace je velice jednoduchá. U tohoto problému by to asi bylo i korektní, ale s kolegou jsme se shodli, že bychom nechtěli v presenteru ručně volat kontrolu existence nového rozšíření a případných dalších funkcí, chceme to přenechat objektu MyApp, u které zavoláme metodu run() a ta provede volání různých metod, které dělají různé systémové záležitosti. Ad 2) vyzkoušel jsem naimplementovat a řešení je opět jednoduché, ale uvědomili jsme si, že není dostačující, protože danou informaci mohu potřebovat i pro změnu např. layoutu stránek, modifikaci menu a možná komplikovanějších záležitostí. Proto jsme zvolili metodu, na kterou jsi mě navedl větou:

je totiz problem, ze instance presenteru se vytvari tady

Naše aplikace z 95% funguje až po obdržení požadavku od uživatele, a tedy po zavolání metody processRequest(), kde se vyvolávají dvě události: onRequest a onPresenter. Proto nám naprosto vyhovuje zavolat MyApp->run() až po odchycení události onPresenter, kde už máme k dispozici vše, co potřebujeme, vč. presenteru.

Takže díky za pomoc a řešení.