Inversion of Control

lopata

Inversion of Control
« kdy: 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).


Rado2

Re:Inversion of Control
« Odpověď #1 kdy: 25. 01. 2016, 19:30:50 »
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ý.

Kit

Re:Inversion of Control
« Odpověď #2 kdy: 25. 01. 2016, 19:32:23 »
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?

lopata

Re:Inversion of Control
« Odpověď #3 kdy: 25. 01. 2016, 19:42:27 »
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á?
 

Kit

Re:Inversion of Control
« Odpověď #4 kdy: 25. 01. 2016, 19:49:48 »
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?


lopata

Re:Inversion of Control
« Odpověď #5 kdy: 25. 01. 2016, 19:58:43 »
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.


lopata

Re:Inversion of Control
« Odpověď #6 kdy: 25. 01. 2016, 20:01:40 »
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ů.

Kit

Re:Inversion of Control
« Odpověď #7 kdy: 25. 01. 2016, 20:10:50 »
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.

Re:Inversion of Control
« Odpověď #8 kdy: 25. 01. 2016, 20:43:03 »
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.

k

Re:Inversion of Control
« Odpověď #9 kdy: 25. 01. 2016, 20:45:01 »
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 :)

Kit

Re:Inversion of Control
« Odpověď #10 kdy: 25. 01. 2016, 20:56:44 »
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.

lopata

Re:Inversion of Control
« Odpověď #11 kdy: 25. 01. 2016, 22:28:08 »
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.   

Kit

Re:Inversion of Control
« Odpověď #12 kdy: 25. 01. 2016, 22:46:26 »
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.

lopata

Re:Inversion of Control
« Odpověď #13 kdy: 25. 01. 2016, 22:58:55 »
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.

lopata

Re:Inversion of Control
« Odpověď #14 kdy: 25. 01. 2016, 23:00:22 »
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.