MVP jak jej dobře chápat, jak mu rozumět?

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

Dobrý den,
prosím Vás, mám problémy ohledně chápání architektury MVC(P), proto bych rád využil toto vlákno na pokládání dotazů ohledně tohoto mého malého problému.
MVC, princim chápu, vím k čemu je Model, k čemu Presenter a dokonce i View :) jenže mě trápí otázky, jako je tahle:

V QuickStartu v kapitole Vytvoření presenter je u podnadpisu Navrhujeme presenter věta: „Mít pouze akce, které souvisejí s jednou činností (tj. např. buď Registrace nebo Přihlašování a Odhlašování, ale už ne obojí) – Jeden presenter by měl pokrývat jeden nebo několik souvisejících Use Case (případ užití)“.

Otázka: Jaké je pravidlo pro dělení Use Case – případu užití? Proč je tu prezenter, který Přihlásí uživatele, odhlasí, ale už jej neregistruje? Z pohledu OOP je to pro mě uživatel a jedna z jeho metod je Registrace, ne? Pokud ne, proč?

Další věc co mě trápí je způsob návrhu aplikace. Na co mám dávat zřetel? Co je pilář celé aplikace v MVC, řekl bych, že to bude Controller, ale když už stojím před úkolem: Naprogramovat Blog a jedna z jeho částí je třída Article, tak už tady se ptám: Kdo je Article? Je to presenter? Je to Model? Nebo jsou to ArticlePresenter i ArticleData? Proč?

Vím, že role Controlleru je řídit, role Modelu je hledat a pokládat k nohám Controlleru data, která žádá a také vím, že View je ten, co s výsledkem běhá sem a tam (metafora vystupu a poskytnutí vstupu), jenže když se dostanu řed ukol, tak se jen donekonečna ptám sám sebe.
Můžete mi prosím poradit?

Mikulas Dite
Člen | 756
+
0
-

Mesiah napsal(a):

Otázka: Jaké je pravidlo pro dělení Use Case – případu užití? Proč je tu prezenter, který Přihlásí uživatele, odhlasí, ale už jej neregistruje? Z pohledu OOP je to pro mě uživatel a jedna z jeho metod je Registrace, ne? Pokud ne, proč?

Registrace se provede jednou pro každého uživatele a je na ní potřeba úplně jiný model než pro přihlášení / ohlášení. Z tohohle hlediska je lepší mít presentery odděleně. Registrace a přihlášení spolu přece vůbec nesouvisí – jediné co mají společné je jeden uživatel a to mají skoro všechny části každé aplikace. Mít všechno v jednom objektu User můžeš. Čím objektověji, tím lépe : ).

Další věc co mě trápí je způsob návrhu aplikace. Na co mám dávat zřetel? Co je pilář celé aplikace v MVC, řekl bych, že to bude Controller, ale když už stojím před úkolem: Naprogramovat Blog a jedna z jeho částí je třída Article, tak už tady se ptám: Kdo je Article? Je to presenter? Je to Model? Nebo jsou to ArticlePresenter i ArticleData? Proč?

Nejsložitější asi bývají modely, částečně proto, že na sobě můžou být závislé. Presentery a view můžeš kdykoliv lehce změnit, ale zaměření byznys funkce (modely) už hůř.

Vím, že role Controlleru je řídit, role Modelu je hledat a pokládat k nohám Controlleru data, která žádá a také vím, že View je ten, co s výsledkem běhá sem a tam (metafora vystupu a poskytnutí vstupu), jenže když se dostanu řed ukol, tak se jen donekonečna ptám sám sebe.

Že model hledá je hodně nadlehčené : ). Můžeš mít model karetní hry, kde karta i balíček jsou modely (a vůbec nesouvisejí s databází) a nic kromě správného tahu nehledají. A nejsem si úplně jistý, jestli se View stará o vstup – ten imho dostává rovnou Controller a měl by ho většinou (upravený) předávat modelům.

Můžete mi prosím poradit?

Asi sem neřekl to hlavní, ale není mi úplně jasné co bych měl vysvětlit. A žádný odkaz taky nemám.

Mesiah
Člen | 240
+
0
-

Jo, takto nějak jsem to potřeboval říct. Teďka jsem nabyl dojmu, že více než poloviční váhu mají v MVC Modely, takže při návrhu aplikace, bych měl dávat největší zřetel na ně?
Doposud jsem při návrhu aplikace dával největší důraz na Presenter, ale z toho co jsi napsal, mám spíš pocit, že Presenter (Controller) vystupuje jen jako prostředník, který nemá zas tak důležitou roli, jak se může na první pohled zdát.

Ještě by mě zajímalo, když vytvářím návrh v MVC, mám pro každou komponentu navrhnout svůj vlastní diagram a pak to „slepit“?

Mimochodem, může mít Model vazbu na Presenter, nebo má jen Presenter vazbu na Model? Jak je to pro View? Je zde striktní omezení?

Tharos
Člen | 1030
+
0
-

Model by teoreticky vůbec neměl vědět o tom, že nějaký presenter existuje, model má být teoreticky „nejnezávislejší“ ze všech vrstev.

Nicméně, porušení MVC/MVP paradigma není nic, za co by se vraždilo, když je prostě důvod pro to, co se dělá. Ono ani Nette samotné není tak striktní a například validace Nette formulářů se děje na úrovni presenteru, přestože učebnicově by se měla odehrávat spíše na úrovni modelu. Architektura MVC má za úkol usnadnit programátorům život. V místech, kde by jej spíše činila složitějším či méně praktickým, určitě není zapotřebí na ní absolutně bazírovat.

Co mi zatím tak připadá, u Nette je největší problém právě s modely, protože Nette v základě sice nevnucuje, ale zároveň ani nenabízí žádný standardní přístup k nim. To není problém v momentě, kdy potřebujeme načíst pár článků a komentářů k nim (prostě vyrobíme nějaké CRUD třídy „Articles“ a „Comments“), nicméně začíná být problémem v momentě, kdy budujeme nějakou robustnější aplikaci. Pak to, že Nette nedefinuje žádnou standardní cestu, osobně vnímám spíše jako nedostatek, protože většina cesta, kterými se dá vydat, je spíše nevhodných.

No, trochu rozvířím vody. Ono totiž existuje hezká poučka, že „model zapouzdřuje stav aplikace“. Mně připadá, že v Nette, kde existují například persistentní parametry u presenteru, stav aplikace zapouzdřuje spíše presenter. Sám preferuji uchovávání stavu aplikace spíše na úrovni modelu, a tak postupuji následovně: presenter přijme od routeru určité parametry (například jazykovou verzi webu, ID stránky v CMS, která se zrovna zobrazuje), a s jejich pomocí inicializuje model. V modelu se nachází třída implementující jakési rozhraní ISiteState, jejímž účelem je čiště udržovat stav modelu. Její instanci si udržuje aktuální presenter a poskytuje ji podřízeným komponentám (presenter implementuje jakési rozhraní ISiteStateHolder, komponenty ISiteStateClient). Od většiny komponent totiž vyžaduji, aby si uchovávaly instanci nějakého pro ně potřebného modelu, který ale bude „vědět“ o nějakém globálním stavu modelu (prostě aby ty komponenty věděly, s jakým ID stránky se momentálně v CMS pracuje, aby se jim pokaždé nemuselo předávat jako parametr v makru control/widget). Momentálně to řeším tak, že komponenty pomocí metody attached monitorují instanci ISiteStateHolder a v případě připojení k nim předávají modelu, který inicializují, instanci implementující ISiteState (původně jsem to řešil přes služby, ale nakonec jsem šel touto cestou, připadá mi čistší).

Snad jsem to popsal aspoň trošku srozumitelně… O co mi ale jde – funguje to hezky, ale pořád mi připadá, že je to takové trošku drbání se levou rukou za levým uchem. Jak řešíte podobné situace? Jak řešíte potřebu uchování stavu aplikace v modelu a případně nějakou persistenci na straně modelu?

Cituji:
„Model je datový a zejména funkční základ celé aplikace. V modelu je schovaná celá aplikační business logika. Je to ono M z MVP (i MVC). Model si lze představit jako stavový prostor, jako množinu různých stavů, do kterých se aplikace může dostat. Jakákoliv událost, jakákoliv akce uživatele (přihlášení, odhlášení, vložení zboží do košíku, změna hodnoty v databázi) představuje jen přechod modelu z jednoho stavu do jiného. Je to černá skříňka, která si uvnitř udržuje aktuální stav a ven nabízí pevně dané rozhraní. Voláním funkcí tohoto rozhraní můžeme zjišťovat či měnit vnitřní stav modelu. Přitom k rozhraní může přistupovat kdokoliv a kdykoliv.“ To je prosím poučka z oficiální Nette dokumentace. :)

Jak tohohle docílit v Nette? Jak uchováváte stav modelu, přenášíte jej mezi requesty?

Editoval Tharos (22. 5. 2010 0:50)

Mesiah
Člen | 240
+
0
-

Děkuji za vyčerpávající odpověď.

…osobně si myslím, že přenášení stavu mezi requesty je špatně. Aspoň na webu. Web je sám o sobě nestavový, je to bráno jako paradigma webu a snažit se jej „znásilňovat a ohýbat“ pomocí různých konstrukcí není dobrým nápadem, ale svět se mění a doba žádá své oběti, svůj vývoj…
Ale to je jen má akademická poznámka.

Tharos
Člen | 1030
+
0
-

Ano, s tímhle plně souhlasím. Nicméně snažím se uchovávat stav na úrovni modelu alespoň v rámci jednoho requestu a připadá mi, že ani v tom mě Nette příliš nepodporuje. Spíše mi připadá, že vede programátora k uchovávání množství parametrů v presenteru a k jejich předávání například komponentám.

Zajímalí by mě, jak jsou na tom místní Nette guru :). Máte někdo vyřešené nějak elegantně uchovávání stavu na úrovni modelu tak, aby k němu měli přístup všechny instance modelových tříd (vytvářených například on-demand v komponentách)?

Jinak ještě jednou k mému příkladu, jde mi konkrétně o příklad CMSka. V šablonách se vykresluje množství komponent (například globální navigace, kontextová navigace a podobně). Většina z těchto komponent potřebuje znát, jaká se zrovna zobrazuje stránka (nějaké ID stránky). V mém případě ID stránky zjistí router a předá jej presenteru (ono by jej teoreticky mohl předat rovnou modelu, ale to taky není optimální, protože pak by se nepřenášelo automaticky mezi odkazy, respektive v presnteru by tak jako tak muselo být). Jde mi to, abych při zápisu {control globalNav} nemusel vždy z presenteru předávat komponentě ono ID strány, ale aby si jej ta komponenta uměla zjistit z nějakého globálního stavu modelu. Teď to vyřešené mám, ale nejsem si jist, zda jsem svým řešením tak nějak nešel mimo koncepci Nette…

Dokumentace, tutoriály, fórum… nic mi nepřineslo odpověď na to, jak by se tohle mělo optimálně řešit. :)

Editoval Tharos (22. 5. 2010 0:51)

Honza Kuchař
Člen | 1662
+
0
-

Myslím si, že to co je psáno v dokumentaci je právě hudba budoucnoti. Nette v této chvíli tak nějak model vypustilo a říká, dělej si to jak chceš. Nápad Tharose se mi zamlouvá a líbí. Myslím, že je to cesta, nicméně cesta reálně-praktická cesta to bude, až bude nějaký předpřipravený model přímo v Nette (IModel, …). Ale pravda, udělat se to dá i teď, akorát to bude více práce a potom to bude téměř zcela jistě totálně nekompatibilní s Davidovým řešením. :-)

Honza Kuchař
Člen | 1662
+
0
-

Hmmm, musíme vymyslet, jak přenášet jakýsi jedničný identifikátor OKNA prohlížeče. Pak půjde uchovávat stav jen pomocí nějakého krátkého ID. (Session se objeví ve všech oknech prohlížeče, to je to se právě nesmí stát) Nicméně mám takový pocit, že je to prostě problém koncepce, protože právě Nette funguje tak jak funguje, ale příkladem opačného principu je třeba ASP.NET Web Forms – postback a pravda, tato metoda se mi příliš nezamlouvá, ale za to umožňuje právě to zachovávání stavu v rámci relace → tato technologie je vhodná na složitější aplikace. Daň za to, každý požadavek = POST požadavek. Alespoň jsem to tak pochopil. Tady je příklad postbackové stránky: www.idos.cz

Pokud se z tohoto kruhu Nette dostane, tak to bude naprostý unikát. A bude to rozhodně další killer feature, která ušetří ale opravdu hodně času. Pro větší aplikace to bude asi největší přínos. Protože tohle je opravdu problém.

Proki
Člen | 66
+
0
-

Tharos napsal(a):
Zajímalí by mě, jak jsou na tom místní Nette guru :). Máte někdo vyřešené nějak elegantně uchovávání stavu na úrovni modelu tak, aby k němu měli přístup všechny instance modelových tříd (vytvářených například on-demand v komponentách)?

Nepatřím mezi Nette guru, ale já tohle zatím řeším tak, že mám nějakou abstraktní třídu, od níž musí povinně všechny ostatní modely dědit. A tahle třída obsahuje statické vlastnosti, které nastavuji např. v BasePresenteru a pak k nim mám přístup už odkudkoliv. Nevím jak moc je to čisté, ale zatím jsem si s tím vždy vystačil.

Mesiah
Člen | 240
+
0
-

Proki napsal(a):

Tharos napsal(a):
Zajímalí by mě, jak jsou na tom místní Nette guru :). Máte někdo vyřešené nějak elegantně uchovávání stavu na úrovni modelu tak, aby k němu měli přístup všechny instance modelových tříd (vytvářených například on-demand v komponentách)?

Nepatřím mezi Nette guru, ale já tohle zatím řeším tak, že mám nějakou abstraktní třídu, od níž musí povinně všechny ostatní modely dědit. A tahle třída obsahuje statické vlastnosti, které nastavuji např. v BasePresenteru a pak k nim mám přístup už odkudkoliv. Nevím jak moc je to čisté, ale zatím jsem si s tím vždy vystačil.

Pokud jsem to pochopil dobře, tak i Thaosovo řešení je založeno na podobném principu, ale v jeho případě používá rozhraní.
Nebo se pletu? :)

Tharos
Člen | 1030
+
0
-

V podstatě ano. Ten můj přístup se hodí v momentě, kdy se může v aplikaci stát, že by se například v rámci jednoho requestu pracovalo paralelně se dvěma stavy modelu (což nemusí být až tak hypotetická situace). Stav modelu je zapouzdřen v instanci nějaké třídy, ne ve statických členech.

Editoval Tharos (22. 5. 2010 15:15)