Fórum Root.cz
Hlavní témata => Vývoj => Téma založeno: lopata 25. 01. 2016, 19:08:46
-
Tak mě napadlo použít IoC na celou aplikaci. Dám příklad - chci zobrazit nějakou notifikaci, ale místo toho, abych volal nějaké API typu Notificator::getInstance()->showNotification(new Notification("NAZDAR")), tak pošlu skrz jasně definovaný kanál nějakou událost např. NotificationRequest a nechám systém, ať si s tím poradí, jak chce. Zatím mě nenapadají nějaké nevýhody tohohle přístupu... tedy až na jednu, nějaký menší overhead (volání metody vs vyslání eventu).
-
Tento prístup sa hodí na to, čo si napísal - posielanie notifikácií o nejakých udalostiach, kde iný systém ich vyhodnocuje. Inak neviem, kam smerovala otázka, príklad je príliš všeobecný.
-
Tak mě napadlo použít IoC na celou aplikaci. Dám příklad - chci zobrazit nějakou notifikaci, ale místo toho, abych volal nějaké API typu Notificator::getInstance()->showNotification(new Notification("NAZDAR")), tak pošlu skrz jasně definovaný kanál nějakou událost např. NotificationRequest a nechám systém, ať si s tím poradí, jak chce. Zatím mě nenapadají nějaké nevýhody tohohle přístupu... tedy až na jednu, nějaký menší overhead (volání metody vs vyslání eventu).
Co je to za jazyk? Vypadá to jako PHP.
v podstatě by aplikace IoC v daném případě měla být OK, jen ten původní příklad vypadá tak odstrašující, že bych ho určitě nepoužil. Nevím, proč se ta třída jmenuje Notification - spíš by se mohla jmenovat Message a mít metodu notify(), která by zprávu zveřejnila. Ovšem v tom případě by bylo nutné do té metody přidat jako parametr instanci toho notifikátoru.
Obávám se, že si tím postupem moc nepomůžeš. Jak by to vypadalo konkrétně podle tebe?
-
Jedná se o docela složitou GUI aplikaci v C++.
Další případ by bylo např. otevření souboru. Uživatel pokliká na nějaký soubor a místo explicitního volání nějaké metody typu EditorUtility::openFile("file")
by se vyslala událost RequestOpenFile a např. nějaký plugin by se postaral o vyřízení.
Výhodu vidím velkou právě např. v tom, že komponenty budou na sobě nezávislé - nějaký file explorer může být teoreticky použit úplně kdekoliv, pokud tam bude komponenta, která se postará o zahandlování události RequestOpenFile.
Další výhodou snadné zaměnění funkcionality, které se stará o handlování RequestOpenFile.
Je to taková forma Dependency Injection /Inversion of Control, kdy komponenta sama si nezajišťuje vyřízení, ale postará se o to někdo jiný.
Tak mě to napadlo, třeba se to někde už používá?
-
V čem vidíš nezávislost komponent? Nevidím žádné IoC, ale jen skrytí závislosti do nějaké netestovatelné třídy.
Co by dělala událost RequestOpenFile? Jak by byla definována závislost na editoru? Nějaký příklad?
-
V čem vidíš nezávislost komponent? Nevidím žádné IoC, ale jen skrytí závislosti do nějaké netestovatelné třídy.
Co by dělala událost RequestOpenFile? Jak by byla definována závislost na editoru? Nějaký příklad?
IoC to je v tom, že já jako vývojář file exploreru se nezajímám o to, kdo nebo jak otevře daný soubor, kde zobrazí jeho obsah, co s tím vlastně udělá. Já jen vyšlu nějakou konkrétní událost RequestOpenFile, která bude mít jako datový člen třeba nějaký identifikátor souboru a nechám aplikaci, ať se o to postará.
V jiném modulu budu vyvíjet třeba nějaký file view a jednoduše se přihlásím na odběr událostí RequestOpenFile.
-
V čem vidíš nezávislost komponent? Nevidím žádné IoC, ale jen skrytí závislosti do nějaké netestovatelné třídy.
Co by dělala událost RequestOpenFile? Jak by byla definována závislost na editoru? Nějaký příklad?
IoC to je v tom, že já jako vývojář file exploreru se nezajímám o to, kdo nebo jak otevře daný soubor, kde zobrazí jeho obsah, co s tím vlastně udělá. Já jen vyšlu nějakou konkrétní událost RequestOpenFile, která bude mít jako datový člen třeba nějaký identifikátor souboru a nechám aplikaci, ať se o to postará.
V jiném modulu budu vyvíjet třeba nějaký file view a jednoduše se přihlásím na odběr událostí RequestOpenFile.
A nezávislost je jakoby v tom, že můžu klidně vzít ten file explorer a použít ho v jiné aplikaci a nemusím řešit, že bych musel vzít taky XY dalších tříd, protože v implementaci file exploreru nepoužívám žádné konkrétní rozhraní, pokud chci něco udělat. Jen pošlu událost.
Tzn. je tam nějaký EventBroker, skrz který jdou všechny události. Takže kombinace IoC a Pub/Sub patternů.
-
Já jen vyšlu nějakou konkrétní událost RequestOpenFile, která bude mít jako datový člen třeba nějaký identifikátor souboru a nechám aplikaci, ať se o to postará.
V jiném modulu budu vyvíjet třeba nějaký file view a jednoduše se přihlásím na odběr událostí RequestOpenFile.
Tak ta poslední věta měla být jako první, protože bez přihlášení nějakého konzumenta události nemá smysl žádnou událost vysílat. Teprve teď to dává nějaký smysl.
Nějaký overhead v tom nevidím, prostě jen přibyla jedna mezivrstva, která nemusí mít na výkon aplikace vůbec žádný vliv. Zato může mít vliv (pozitivní či negativní) na složitost aplikace.
-
Hezke, ale to, co popisujes, neni IoC ale Observer a abstrakce.
IoC je, ze mas servicu a ta pouziva dalsi servicy. Ty servicy, ktere pouziva, neinstancuje ani na ne neziskava odkaz pres service locator. Ma na ne odkazy, ktery se do ni dostanou v dobe jeji tvorby prostrednictvim seteru, konstruktoru... O to se stara nejaky kontejner, nebo to muzes klidne delat rucne.
Podstatna vyhoda - v dobe testovani tam muzes injektovat mocky.
-
Tak mě napadlo použít IoC na celou aplikaci. Dám příklad - chci zobrazit nějakou notifikaci, ale místo toho, abych volal nějaké API typu Notificator::getInstance()->showNotification(new Notification("NAZDAR")), tak pošlu skrz jasně definovaný kanál nějakou událost např. NotificationRequest a nechám systém, ať si s tím poradí, jak chce. Zatím mě nenapadají nějaké nevýhody tohohle přístupu... tedy až na jednu, nějaký menší overhead (volání metody vs vyslání eventu).
Tohle je zasílání zpráv, ne IoC.
A nezávislost je jakoby v tom, že můžu klidně vzít ten file explorer a použít ho v jiné aplikaci a nemusím řešit, že bych musel vzít taky XY dalších tříd, protože v implementaci file exploreru nepoužívám žádné konkrétní rozhraní, pokud chci něco udělat. Jen pošlu událost.
Dříve nebo později ti přestane stačit string a budeš potřebovat poslat nějakou větší strukturu. To se neobejde bez nějaké sdílené definice struktury a už tam máš závislost :)
-
Dříve nebo později ti přestane stačit string a budeš potřebovat poslat nějakou větší strukturu. To se neobejde bez nějaké sdílené definice struktury a už tam máš závislost :)
Stačí mít v rozhraní jednu metodu, která tuto strukturu odprezentuje příjemci. Příjemce o vnitřní struktuře poslaného objektu nepotřebuje znát vůbec nic.
-
Tak mě napadlo použít IoC na celou aplikaci. Dám příklad - chci zobrazit nějakou notifikaci, ale místo toho, abych volal nějaké API typu Notificator::getInstance()->showNotification(new Notification("NAZDAR")), tak pošlu skrz jasně definovaný kanál nějakou událost např. NotificationRequest a nechám systém, ať si s tím poradí, jak chce. Zatím mě nenapadají nějaké nevýhody tohohle přístupu... tedy až na jednu, nějaký menší overhead (volání metody vs vyslání eventu).
Tohle je zasílání zpráv, ne IoC.
A nezávislost je jakoby v tom, že můžu klidně vzít ten file explorer a použít ho v jiné aplikaci a nemusím řešit, že bych musel vzít taky XY dalších tříd, protože v implementaci file exploreru nepoužívám žádné konkrétní rozhraní, pokud chci něco udělat. Jen pošlu událost.
Dříve nebo později ti přestane stačit string a budeš potřebovat poslat nějakou větší strukturu. To se neobejde bez nějaké sdílené definice struktury a už tam máš závislost :)
Všechno může být součástí definice té zprávy neboť definice zprávy = C++ třída. Každopádně taková závislost nevadí, tam jde primárně o to, že ani file explorer ani file view nebudou muset být psány tak, aby nějakým způsobem jeden explicitně komunikoval s druhým. Takže file view neví nic o file exploreru ani naopak. Oba konce - jak odesilatel tak příjemce zprávy ale vědí, že pro otevření souboru je potřeba pracovat s FileOpenRequest.
Někdo by si mohl např. postahovat definice zpráv pro aplikaci typu XXX a pak komponenty, které s danými definicemi pracuji a pak už to jen poskládat.
Zatím jsem nějaká negativa nenašel. Nesmi se to ale přeceňovat, tzn. pokud budu chtít vytvořit objekt A, tak nebudu vytvářet zprávu typu CreateARequest, ale normálně instancuju třídu co potřebuju. Pokud by se ale jednalo o nějakou service třídu, tak už bych mohl např. v konstruktoru vysílat zprávu typu CreateServiceRequest a měl bych formu DI.
-
Zatím jsem nějaká negativa nenašel. Nesmi se to ale přeceňovat, tzn. pokud budu chtít vytvořit objekt A, tak nebudu vytvářet zprávu typu CreateARequest, ale normálně instancuju třídu co potřebuju. Pokud by se ale jednalo o nějakou service třídu, tak už bych mohl např. v konstruktoru vysílat zprávu typu CreateServiceRequest a měl bych formu DI.
Existují i další vzory, které se na řešení problému mohou hodit lépe. Co třeba na vytváření objektů využít Simple Factory?
Tak trochu začínám být alergický na pojem "servisní třída". Často to poukazuje na chybu v návrhu. Aplikace pak s OOP nemá mnoho společného, protože smysl OOP je v tom, že data a chování jsou zapouzdřena do jednoho objektu. Servisní třídy tento princip narušují. Tam, kde jsou servisní třídy, bývají i anemické objekty. Místo anemických objektů můžeme v klidu použít strukturu nebo kolekci. Najednou z toho máme klasické procedurální programování v objektovém jazyce.
-
Zatím jsem nějaká negativa nenašel. Nesmi se to ale přeceňovat, tzn. pokud budu chtít vytvořit objekt A, tak nebudu vytvářet zprávu typu CreateARequest, ale normálně instancuju třídu co potřebuju. Pokud by se ale jednalo o nějakou service třídu, tak už bych mohl např. v konstruktoru vysílat zprávu typu CreateServiceRequest a měl bych formu DI.
Existují i další vzory, které se na řešení problému mohou hodit lépe. Co třeba na vytváření objektů využít Simple Factory?
Tak trochu začínám být alergický na pojem "servisní třída". Často to poukazuje na chybu v návrhu. Aplikace pak s OOP nemá mnoho společného, protože smysl OOP je v tom, že data a chování jsou zapouzdřena do jednoho objektu. Servisní třídy tento princip narušují. Tam, kde jsou servisní třídy, bývají i anemické objekty. Místo anemických objektů můžeme v klidu použít strukturu nebo kolekci. Najednou z toho máme klasické procedurální programování v objektovém jazyce.
Chci třeba odesílat email, mám nějaké rozhraní IEmailService, tak si požádám o implementaci zasláním toho požadavku a výsledkem bude získaná reference na implementaci IEmailService. Kdybych používal nějaké factory třídy, tak bych je zase musel explicitně někam dosadit. Takhle se odněkud jen přihlásím k odběru eventů CreateServiceRequest a pokud takový event přijde.
-
Zatím jsem nějaká negativa nenašel. Nesmi se to ale přeceňovat, tzn. pokud budu chtít vytvořit objekt A, tak nebudu vytvářet zprávu typu CreateARequest, ale normálně instancuju třídu co potřebuju. Pokud by se ale jednalo o nějakou service třídu, tak už bych mohl např. v konstruktoru vysílat zprávu typu CreateServiceRequest a měl bych formu DI.
Existují i další vzory, které se na řešení problému mohou hodit lépe. Co třeba na vytváření objektů využít Simple Factory?
Tak trochu začínám být alergický na pojem "servisní třída". Často to poukazuje na chybu v návrhu. Aplikace pak s OOP nemá mnoho společného, protože smysl OOP je v tom, že data a chování jsou zapouzdřena do jednoho objektu. Servisní třídy tento princip narušují. Tam, kde jsou servisní třídy, bývají i anemické objekty. Místo anemických objektů můžeme v klidu použít strukturu nebo kolekci. Najednou z toho máme klasické procedurální programování v objektovém jazyce.
Chci třeba odesílat email, mám nějaké rozhraní IEmailService, tak si požádám o implementaci zasláním toho požadavku a výsledkem bude získaná reference na implementaci IEmailService. Kdybych používal nějaké factory třídy, tak bych je zase musel explicitně někam dosadit. Takhle se odněkud jen přihlásím k odběru eventů CreateServiceRequest a pokud takový event přijde.
Jsem to nějak nedopsal. Prostě ten CreateServiceRequest je v podstatě takové explicitnější Dependency Injection.
-
... místo toho, abych volal nějaké API typu Notificator::getInstance()->showNotification(new Notification("NAZDAR")), tak pošlu skrz jasně definovaný kanál nějakou událost např. NotificationReques...
Prva vec, nad ktoru sa treba vzdy zamysliet je, ci sa to neda nahradit niecim ako notify("NAZDAR"). Viem, ze islo o priklad a davam to tiez ako priklad. Kod treba pisat co najjednoduchsie a nie co najzlozitejsie. Ked nepotrebujem v celej aplikacii IoC, tak nebudem robit v celej aplikacii IoC.
-
... místo toho, abych volal nějaké API typu Notificator::getInstance()->showNotification(new Notification("NAZDAR")), tak pošlu skrz jasně definovaný kanál nějakou událost např. NotificationReques...
Prva vec, nad ktoru sa treba vzdy zamysliet je, ci sa to neda nahradit niecim ako notify("NAZDAR"). Viem, ze islo o priklad a davam to tiez ako priklad. Kod treba pisat co najjednoduchsie a nie co najzlozitejsie. Ked nepotrebujem v celej aplikacii IoC, tak nebudem robit v celej aplikacii IoC.
Také si to myslím a z daného příkladu bych to udělal asi takto:
notificator = new Notification(Notificator::getInstance());
// ...
notificator->show("NAZDAR");
DI je na prvním řádku, kde je uvedeno, kam bude objekt notificator posílat zprávy. Takových objektů je možné vyrobit víc, další může být např. objekt errorNotificator, který bude zprávy posílat jinam než ten první. Se dvěma notifikátory už se dá vyřádit docela dobře a dokonce i metoda show() může být mnohonásobně přetížena, aby se dalo notifikovat prakticky cokoli.
-
... místo toho, abych volal nějaké API typu Notificator::getInstance()->showNotification(new Notification("NAZDAR")), tak pošlu skrz jasně definovaný kanál nějakou událost např. NotificationReques...
Prva vec, nad ktoru sa treba vzdy zamysliet je, ci sa to neda nahradit niecim ako notify("NAZDAR"). Viem, ze islo o priklad a davam to tiez ako priklad. Kod treba pisat co najjednoduchsie a nie co najzlozitejsie. Ked nepotrebujem v celej aplikacii IoC, tak nebudem robit v celej aplikacii IoC.
Také si to myslím a z daného příkladu bych to udělal asi takto:
notificator = new Notification(Notificator::getInstance());
// ...
notificator->show("NAZDAR");
DI je na prvním řádku, kde je uvedeno, kam bude objekt notificator posílat zprávy. Takových objektů je možné vyrobit víc, další může být např. objekt errorNotificator, který bude zprávy posílat jinam než ten první. Se dvěma notifikátory už se dá vyřádit docela dobře a dokonce i metoda show() může být mnohonásobně přetížena, aby se dalo notifikovat prakticky cokoli.
Ale to je jedno, klidně to může být třeba jenom notify("Nazdar"). To co jsem uváděl je příklad nějaké klasické implementace bez toho mého sporného IoC, o kterém by se dalo polemizovat, jestli to teda je IoC nebo ne. Ta pointa není v tom, jestli si někdo udělá nějakou helper metodu notify, aby všude nemusel psát těch xy znaků. Ta pointa je v tom, že se používá nějaké třída Notificator, která má nějakou implementaci, třeba zobrazovaní někam do rohu okna apod..
Ale já nechci použávat nějakou konkrétní implementaci. Ja chci zobrazit zprávu, to je všechno. Jak se zobrazí, kde, jak velká apod., to je jedno. Takže já místo abych používal nějakou konkrétní implementaci Notificator, tak pošlu zprávu ShowNotificationRequest a nechám nějaký plugin nebo jakoukoliv jinou část systému, aby se postarala o zpracování této žádosti.
EventBroker::sendEvent(new ShowNotificationRequest(new Notification("Nazdar")))
-
Všechno může být součástí definice té zprávy neboť definice zprávy = C++ třída.
Pod pojmem nezávislost si představuji že především nemusím shánět nějaké .h s definicí čehokoliv :)
Chci třeba odesílat email, mám nějaké rozhraní IEmailService, tak si požádám o implementaci zasláním toho požadavku a výsledkem bude získaná reference na implementaci IEmailService. Kdybych používal nějaké factory třídy, tak bych je zase musel explicitně někam dosadit. Takhle se odněkud jen přihlásím k odběru eventů CreateServiceRequest a pokud takový event přijde.
Je to to, že nechcete vytvářet instanci takhle
my_mail_instance = new MailSender()
ale chcete někam vyslat zprávu central_brain_of_mankind.CreateRequest(...), aby vám v odpovědi ResponseRequest přišla instance ?
To můžete rovnou napsat dynamic_cast<IEmailService>(central_brain_of_mankind.GetInstance("EmailService)) a ušetříte spoustu psaní :-)
-
EventBroker::sendEvent(new ShowNotificationRequest(new Notification("Nazdar")))
dynamic_cast<INotificationService>(central_brain_of_mankind.GetInstance("NotificationService)).Show("Nazdar");
-
Chci třeba odesílat email, mám nějaké rozhraní IEmailService, tak si požádám o implementaci zasláním toho požadavku a výsledkem bude získaná reference na implementaci IEmailService. Kdybych používal nějaké factory třídy, tak bych je zase musel explicitně někam dosadit. Takhle se odněkud jen přihlásím k odběru eventů CreateServiceRequest a pokud takový event přijde.
Je to to, že nechcete vytvářet instanci takhle
my_mail_instance = new MailSender()
ale chcete někam vyslat zprávu central_brain_of_mankind.CreateRequest(...), aby vám v odpovědi ResponseRequest přišla instance ?
To můžete rovnou napsat dynamic_cast<IEmailService>(central_brain_of_mankind.GetInstance("EmailService)) a ušetříte spoustu psaní :-)
Jo přesně tak, konečně to někdo pochopil. Když požadavek bude chtít nějakou odpověď, tak pro ni bude místo už v tom požadavku, takže by ten požadavek měl třeba datový člen EmailService* resolvedService nebo tak něco.
Ta varianta s tím dynamic castem je druhá možnost, že tam bude někde něco jako ServiceRepository nebo tak něco. Akorát ty eventy mají výhodu v tom, že na ně může teoreticky reagovat víc částí, můžu logovat veškerou aktivitu aplikace, můžu event potlačit, prostě by to bylo flexibilnější.
Psaní by se ani moc nemuselo ušetřit.
-
Teoreticky může na zprávu reagovat víc částí, prakticky budete mít v aplikaci jednu DLLku s EmailService :-)
Tudíž se můžete navrátit ke klasice v EmailService.dll jedna exportovaná C funkce CreateEmailServiceInstance a ta vrací pointer na IEmailService.
Popřípadě k CoCreateInstance.
-
Jedná se o docela složitou GUI aplikaci v C++.
Další případ by bylo např. otevření souboru. Uživatel pokliká na nějaký soubor a místo explicitního volání nějaké metody typu EditorUtility::openFile("file")
by se vyslala událost RequestOpenFile a např. nějaký plugin by se postaral o vyřízení.
Výhodu vidím velkou právě např. v tom, že komponenty budou na sobě nezávislé [...]
Tak mě to napadlo, třeba se to někde už používá?
Ano, přesně takhle to funguje v Erlangu.
-
Trochu mícháte dvě věci, design patterns a application patterns, viz zde https://msdn.microsoft.com/en-us/library/ee413983.aspx, nebo zajímavý vzor "pumpy a filtry", viz zde http://www.enterpriseintegrationpatterns.com/patterns/messaging/PipesAndFilters.html. A další tamtéž.
-
Trochu mícháte dvě věci, design patterns a application patterns, viz zde https://msdn.microsoft.com/en-us/library/ee413983.aspx,
Bylo by dobré nemíchat patterny s Microsoftem. To opravdu nejde dohromady.