Přejít k navigační liště

Zdroják » Různé » Úvod do architektury MVC

Úvod do architektury MVC

Články Různé

MVC je zajímavý fenomén – jeho popis najdete v tisících článků, a přesto kolem něj stále panuje víc zmatení než pochopení. Cílem této třídílné série proto bude podat poctivý, snad v něčem i unikátní úvod do světa MVC a souvisejících prezentačních vzorů. Dnes začneme obecnými principy architektury MVC.

Nálepky:

Architektura MVC (Model-View-Controller) v posledních letech dramaticky nabírá na popularitě, což je nejspíš způsobeno tím, že jednotlivé technologie již mají ranou fázi vývoje za sebou a vedle „základní výbavy“ se tak snaží nabídnout i prostředky pro tvorbu aplikací s kvalitní architekturou. Zend proto investuje nejen do PHP samotného, ale i do Zend Frameworku. Microsoft proto vytvořil nový webový framework ASP.NET MVC a i dalších nezávislých tvůrců MVC frameworků pro různé technologie jsou stovky. MVC je zkrátka žhavé téma současnosti, a to jsme teprve na začátku jeho nástupu do mainstreamu.

Architektura MVC má jednu zajímavou vlastnost: základní ideje člověk pochopí během pár minut, ale plně proniknout do všech detailů může trvat měsíce i roky. Jak se stává MVC populárnějším a populárnějším, na webu i v knihách se objevují jeho popisy, které se však podle mé zkušenosti spíše zaměřují na onu několikaminutovou část. Ucelených úvodů do MVC je málo a důsledkem je, že o MVC mají různí vývojáři různé představy a často narazíte na „MVC aplikaci“, která vlastně nemá s tímto vzorem vůbec nic společného.

Mým hlavním cílem proto bude popsat MVC a související prezentační vzory tak, abyste si odnesli co nejucelenější obrázek. Nečekejte vědecký popis s přísně formální strukturou, historickými údaji a podobně; články v této minisérii píšu tak, aby byly z pohledu vývojáře co nejužitečnější.

Pojďme tedy na to.

Motivace aneb problémy drag & drop vývoje

Protože dnes existují frameworky se stovkami předpřipravených komponent a protože tyto komponenty můžeme snadno uspořádat pomocí vizuálního návrháře v moderním IDE, lze snadno podlehnout iluzi, že tvorba UI je hračkou.

Ano, je, ale pouze do chvíle, než:

  • potřebujete najít bug v obrazovce, která má ve zdrojové podobě tisíce řádků kódu
  • máte zobrazit stejná data několikrát (např. seznam zákazníků a detail zákazníka)
  • potřebujete zajistit, aby se vám kód zbytečně neopakoval
  • potřebujete aplikaci pokrýt automatizovanými testy

V praxi a zvlášť na větších projektech proto rychle zjistíte, že pohodlí drag&drop vývoje vždy draze zaplatíte jinými komplikacemi, které se nejcitelněji projeví ve fázi údržby aplikace. Protože je tato fáze z pohledu životního cyklu aplikace nejdelší, nejnáročnější a tedy i nejdražší (i pokud „platíte“ pouze vlastním časem), téměř vždy se vyplatí oželet některé vymoženosti IDE a radši věnovat energii kvalitnímu návrhu aplikace.

V průběhu posledních desetiletí proto bylo popsáno několik architektonických vzorů pro prezentační vrstvu (zkráceně „prezentační vzory“, „GUI architektury“ a podobně), které řeší jedinou věc: jak udělat prezentační vrstvu co nejudržovatelnější. Právě o nich je celá tato minisérie.

MVC v pěti minutách

Jak jsem již zmínil, krása i zákeřnost architektury MVC spočívá v tom, že ji lze zdánlivě úspěšně vysvětlit jedním odstavcem. Posuďte sami:

Architektura MVC dělí aplikaci na 3 logické části tak, aby je šlo upravovat samostatně a dopad změn byl na ostatní části co nejmenší. Tyto tři části jsou Model, View a Controller. Model reprezentuje data a business logiku aplikace, View zobrazuje uživatelské rozhraní a Controller má na starosti tok událostí v aplikaci a obecně aplikační logiku.

Snadné, že? Jak se ale říká, ďábel je ukrytý v detailech. Při reálném vývoji budete řešit konkrétní otázky a sami sebe se budete ptát: Patří prezentační logika do View nebo do Controlleru? Patří funkčnost aplikace do Controlleru nebo do Modelu? Jak úzká vazba panuje mezi View a Controllerem? Má mít View přímou vazbu na Model? Tyto otázky jsou zajímavé tím, že z teoretického pohledu nejsou položeny moc dobře (jsou vágní a nejdou zodpovědět jednoznačně), ale ptají se přesně na věci, které programátor potřebuje řešit. Není v tom rozpor? Jak by bylo MVC užitečné, kdyby neumělo dát vývojáři konkrétní odpovědi na konkrétní otázky?

Zde se dostáváme k něčemu, co je pro pochopení MVC zásadní. Hned na začátku je totiž potřeba oddělit 2 věci:

  • MVC jako obecnou architekturu (přístup k tvorbě aplikací)
  • a potom jednotlivé variace MVC

Špatnou zprávou je, že variací je několik a že právě v jejich ovládnutí spočívá komplikovanost MVC, dobrou zprávou je, že právě konkrétní variace jsou vývojáři bezprostředně užitečné. Podrobně se jim proto budeme věnovat příště, nyní ale pojďme na popis MVC jakožto obecné architektury.

MVC pod drobnohledem

Už tedy víme, že architektura MVC dělí aplikaci do třech logických částí, kterými jsou:

  • Model
  • View
  • Controller

Tyto části se pokusím popsat na následujícím příkladu, kde se záměrně vyhýbám konkrétní programátorské technologii a za pomocníka si beru Excel. Podívejte se na následující obrázek:

Excel s tabulkou a grafy

Model je v tomto případě datová struktura, která drží čísla 105, 80 a tak dále. V aplikaci může být realizována jako pole čísel nebo třeba pomocí třídy s názvem Profit, konkrétní realizace není na této úrovni podstatná. Rovněž si všimněte, že příklad obsahuje průměr – tento výpočet rovněž logicky patří do modelu. Model jsou tedy data plus business logika (někdy zvaná doménová logika) aplikace. Teoreticky může být model pouhou sadou datových objektů bez business logiky, ale to je poměrně netypické.

View najdeme v příkladu hned několik. Určitě byste nepřešli bez povšimnutí čtyři různé grafy prezentující stejná data, ale i ona tabulka s čísly sama o sobě je dalším pohledem (View) – mohl bych tomuto pohledu například nastavit, aby čísla zobrazoval se znakem měny a podobně. View je tedy zobrazením modelu a dalších prvků uživatelského rozhraní (například záhlaví „Průměr“ žije čistě ve View a nemusí mít s Modelem nic společného).

Controller je nejhůře představitelnou částí na uvedeném příkladu. Projeví se ve chvíli, kdy upravím nějakou buňku. Controller má v tu chvíli na starost, aby se aktualizoval model, došlo k výpočtu nového průměru a překreslily se všechny View (pozor, to, jak se změny v Modelu projeví ve View, nemusí mít s Controllerem vůbec nic společného, Controller pouze tento proces spouští). Controller je tedy jakousi ústřední výkonnou jednotkou, která se stará o celkové provázání funkčnosti aplikace. Jak asi cítíte z této vágní definice, u Controlleru je poměrně velký prostor pro jeho upřesnění v jednotlivých variacích. Je tomu skutečně tak – právě pojetím Controlleru se jednotlivé vzory od sebe nejvíce liší, ale o tom až příště.

Návaznost jednotlivých komponent ilustruje následující obrázek:

Schéma základních vztahů v MVC

Jak naznačují šipky, v architektuře MVC v principu existují pouze dvě přímé vazby:

  • Controller má přímý odkaz na Model, aby mohl upravit jeho data
  • View má přímý odkaz na Model, aby mohl jeho data zobrazit

Všimněte si, že to je vše. Žádné další vazby nejsou na této úrovni podstatné, ačkoliv v praxi a v závislosti na konkrétní variaci MVC je ještě poměrně častá vazba mezi Controllerem a View (někdy jednosměrná, někdy obousměrná).

Co naopak nikdy nesmí existovat, je přímá vazba Modelu na ostatní dvě komponenty. V některých schématech uvidíte „nepřímou vazbu“ z Modelu na View, čímž se má na mysli, že když se data Modelu změní, příslušná View jsou upozorněna nějakým notifikačním mechanismem (např. pomocí vzoru Observer). V žádném případě však Model nemůže držet přímý odkaz na View nebo na Controller – to by byla hrubá chyba v návrhu aplikace.

Interakce s uživatelem

Jak do architektury MVC zapadá komunikace s uživatelem? Podívejte se na následující schéma:

Schéma interakce MVC s uživatelem

Výstup aplikace má vždy na starost View, ale složitější je to s uživatelským vstupem. Zde existují dvě principiálně odlišné možnosti:

  • U „widgetových“ systémů (komponentové frameworky typu Java Swing, Windows Forms, WPF, Silverlight, Flex, ASP.NET Web Forms, PRADO v PHP a podobně) umí uživatelský vstup ošetřit komponenty samy – například tlačítko umí reagovat na událost Click, textové pole dokáže zachytávat, co uživatel píše na klávesnici a podobně. Zde tedy uživatelský vstup primárně zachycuje View.
  • Pak máme systémy „newidgetového“ typu, kde žádné komponenty neexistují a ošetření uživatelského vstupu je tak potřeba učinit někde jinde. V příštím díle se podíváme, jaké konkrétně tyto systémy jsou, ale pro teď stačí říct, že v tom případě je to Controller, kdo zpracovává uživatelský vstup.

Zpracování uživatelského vstupu je pro dělení prezentačních vzorů hodně důležité, ale i o tom až příště.

Pár poznámek k Modelu

Ačkoliv MVC jako celek řeší prezentační vrstvu aplikace, pouze dvě komponenty mají přímo na starost prezentační funkcionalitu – jsou to View a Controller. Toto jsou také komponenty, které se v jednotlivých variacích liší, zatímco Model je stále stejný, a proto k němu můžu uvést pár poznámek už tady:

  • Nejčastější chybou začátečníků je, že za Modelem vidí pouze sadu datových objektů (možná proto, že v data-centrickém programování se „datový model“ skutečně orientuje primárně na datové struktury). Model ve smyslu MVC je však daleko víc, je to tzv. doménový model, který modeluje vztahy reálného světa. V něm jsou kromě dat důležité i další věci, jako například business pravidla („zákazník může otevřít novou objednávku až po zaplacení minulé faktury“) nebo validační pravidla („jméno je povinná položka“). Některé technologie svádí k definici doménových pravidel ve View (např. validátory v ASP.NET nebo ve Flexu), v architektuře MVC však správně patří do Modelu. Důvod je ten, že jednotlivá pravidla typicky potřebujeme ve více pohledech současně (např. při vytvoření nového zákazníka a pak při jeho editaci), a jejich uložení ve View by znamenalo nutnost duplikace. Proto se tento ochuzený model považuje za architektonickou chybu a Fowler ho řadí mezi antivzory pod názvem anemický doménový model.
  • O Modelu se většinou mluví v jednotném čísle, i když je obvykle realizován více objekty.
  • V klasickém třívrstvém modelu, kde máme datovou, aplikační a prezentační vrstvu, Model v prezentační vrstvě v principu odpovídá modelu v aplikační vrstvě. Bohužel není zcela obvyklé, aby nástroje uměly tyto modely synchronizovat – tento úkol je tak většinou na bedrech programátora.

Pokračování příště

Po dnešku máte dobrou představu o základní architektuře MVC, o základních vazbách mezi jednotlivými komponentami a o významu vrstvy zvané Model. Příště se podíváme na konkrétní variace MVC, kde to teprve všechno začne být zajímavé.

Používáte MVC?

Komentáře

Subscribe
Upozornit na
guest
59 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
View all comments
danaketh

Super. Tohle je přesně to co jsem potřeboval k ujasnění některých vazeb v MVC. Těším se na další díly.

psonek

Z teoretické stránky se v MVC zas tak moc neorientuju, ale prakticky bych řekl, že umím napsat MVC aplikaci. Tohle jsou moje pravidla:

  • Dobrý model je základ. Používat silně typované kolekce, přístup k položkám modelu vždy přes properties.
  • Pro view je model read-only. Nikdy nemodifikovat model přímo v kódu view.
  • V GUI na každou uživatelskou akci jednořádková obsluha. Pokud má funkce button_Click víc než jeden řádek, je to špatně.
  • Definovat View jako interface. Ten interface pak musí být možné implementovat např. jako formulář, webovou stránku nebo v konzoli.

Ze začátku je potřeba odolat pokušení měnit model přímo z view. A pak asi nejtěžší je navrhnout View interface. Buď je možnost si ho dobře promyslet předem nebo ho dělat stylem ad-hoc (postupně přidávat a měnit funkce).

Typický příklad editace osoby:

View::address_Validating() {
  controller.setAddress(addressTextBox.Text);
}

Controller::setAddress(Person p, string newAddress, IView view) {
  if(address == NULL) {
    view.OnEmptyAddress(newAddress);
    return;
  }
  p.Address = address;
  view.OnAddressChanged(p, newAddress);
}

View::OnEmptyAdress(Person p) {
  ShowMessageBox("Empty address!!");
}

View::OnAddressChanged(Person p, string newAddress) {
  addressTextBox.Text = newAddress;
}
Lojza

Jo jo, moc se mi líbí, že ui event 'validace adres' je okoučováno v Controller::setAddress. :-))

ava

Hmm, prijde mi (i kdyz neznam kontext, pouzity jazyk ani framework :-), ze cely uvedeny priklad je tak komplikovany pouze proto, ze v sobe zahrnuje validaci – kontrolu toho, zda adresa neni prazdna, a pripadne osetreni. Kdyby mohla byt adresa libovolna, tak by mel priklad 0 radku, jestli to chapu spravne? (predpokladam ze samotne navazani view a controlleru na model tak, aby bylo mozne ve vstupnim policku editovat adresu, je v tomto priklade provedeno jinde, napr. v GUI builderu).

Potom podle me doslo presne k te chybe, na kterou je upozorneno i v clanku – validacni logika neni v modelu, ale ve view/controller.

Kdysi jsem psal mirne rozsahlou GUI aplikaci v pythonu (>100 oken), udelal jsem si vlastni MVC miniframework, ktery toto resil tak, ze kazdy model mel validacni metodu, ktera vracela kolekci aspektu ktere neprosly validaci, a popisu prislusnych chyb. Dialog potom pred acceptnutim volal funkci validate, ktera provedla validaci modelu, nasla k prislusnym nevalidnim aspektum prislusna views, ty vycervenila a prvni z nich focusla (aby uzivatel mohl pohodlne opravit chyby), a zobrazila errorbox se seznamem chyb v dialogu. Timto zpusobem byla oddelena prezentace chyb od samotne validace, veskera validace byla v modelu (bylo mozno zjistit zda je korektni i bez GUI), a do view/controlleru jsem nemusel nic cpat. Prislo mi to jako dobre reseni.

Omlouvam se za priserne vmixovavani cechoanglismu do prispevku :)

smilelover

Potom podle me doslo presne k te chybe, na kterou je upozorneno i v clanku – validacni logika neni v modelu, ale ve view/controller.

Pozor! V clanku se pise, ze validacni pravidla maji byt vylucne v modelu, ne logika. Snazit se do modelu cpat i veskerou validacni logiku je casto drbani se levou rukou za pravym uchem.

U webovych aplikaci se regulerne a spravne resi validace na urovni C ci V, problem je, kdyz i ta pravidla jsou na techto vrstvach definovana a ony si je nezadaji od M (jako treba delky poli ci regexpy natvrdo zapsane primo v JavaScriptech pro validaci pred odeslanim formu).

ava

Hmm, neznam presnou definici 'validacni logiky' ani 'validacnich pravidel', tak se omlouvam za pripadne zmateni. Mel jsem na mysli 'programovy kod, ktery se vykona a overi, zda je model validni ci ne, a pokud neni, dokaze podat (nikoliv prezentovat) informaci o tom, co je spatne'.

V klasicke newebove aplikaci neni problem toto mit pouze v modelu. Jestli to chapu spravne, ve webovych aplikacich to problem byt muze, protoze model a jeho logika jsou ulozeny na serveru a odezva (pripadne pruchodnost site) by byly neumerne vysoke (je tu jeste jiny principialni problem? O webovych aplikacich moc nevim)

Asi nejcistsi reseni by bylo v takovem pripade bylo automaticky generovat javascriptovy client-side validacni kod ze zdrojaku validacniho kodu v modelu, aby se programator vyhnul duplicitam, ale nevim jestli to nekdo tak dela :) Pokud me nekdo pouci o tom, jak se toto dilema (duplikace kodu vs. odezva/propustnost) dnes resi, a jak by se spravne resit melo, budu jen rad..

smilelover

'programovy kod, ktery se vykona a overi, zda je model validni ci ne, a pokud neni, dokaze podat (nikoliv prezentovat) informaci o tom, co je spatne'.

No, a ten kod k tomu overeni pouziva nejaka pravidla. Ta musi byt ulozena v Modelu, resp. presneji receno, Model o ne musime zadat. Ta logika muze byt IMHO kdekoliv a taky casto byva, protoze tlacit ji vzdy do modelu neni prakticke.

V klasicke newebove aplikaci neni problem toto mit pouze v modelu. Jestli to chapu spravne, ve webovych aplikacich to problem byt muze, protoze model a jeho logika jsou ulozeny na serveru a odezva (pripadne pruchodnost site) by byly neumerne vysoke (je tu jeste jiny principialni problem? O webovych aplikacich moc nevim)

Ne, na serveru to klidne muze byt, ale treba v Controlleru, ne nutne v Modelu. I kdyz, kdyz se to tak vezme — validacni logika bude mit vzdy nejaky opakujici se kus kodu, takze nebude primo v controlleru, ale v nejakem validacnim manazerovi, ktery budou jednotlive controllery volat. A to uz je v podstate v Modelu.
Ale prikladem je treba validace pomoci anotaci na properties v controlleru (tam zas ovsem neni vzdy jednoduche rozumne ta pravidla dostat, alespon z pohledu Javy a jejiho chovani v pripade anotaci).

Asi nejcistsi reseni by bylo v takovem pripade bylo automaticky generovat javascriptovy client-side validacni kod ze zdrojaku validacniho kodu v modelu, aby se programator vyhnul duplicitam, ale nevim jestli to nekdo tak dela :)

Priznam se, ze taky ne vzdy, ale vidim to jako idealni reseni. Vykonovym problemum se da vyhnout tak, ze se to negeneruje on the fly, ale pri deployi nejakym skriptem (Maven, Ant, Phing… moznosti je plno).

ava

ad. 1)
Nechapu vyznam pravidel, z meho pohledu kod = pravidla, o nic nezadam, proste spustim validacni metodu ktera je (a mela by) byt ulozena v modelu. U GUI aplikaci nevidim duvod, proc by to nemelo byt prakticke, krome spatne navrzeneho frameworku. Ponechme ted prosim stranou (opravdu, prosim, nebylo by to prinosem) napr. ulozene procedury v relacnich DB ktere kontroluji jeji konzistenci

ad. 2)
Proc by mela mit opakujici se kus kodu? Nevidim duvod, v mnou vyse zminene aplikaci se nic takoveho nestalo (pokud ano, bylo to mym opomenutim, nikoliv z principialniho donuceni)
DialogController v mem pripade delal pouze to, ze kdyz se uzivatel pokusil acceptnout dialog, tak provedl neco nasledujiciho (jde o myslenku):

validationResult = model.validate()
if not validationResult.isValid:
….self.highlightAndFocusInvalidWidgetsFrom(validationResult)
….self.showError(validationResult.errorText())
else:
….self.accept()

od te doby jsem NIKDY nemusel psat validaci nikam jinam nez do prislusnych model.validate(), a zadarmo jsem mel upozornovani uzivatele na chybna policka atp..

O validaci pomoci anotaci na properties v controlleru v Jave totalne nic nevim, ale nezni to moc lakave :-)

ad 3)
Problemem samozrejme neni vykon, ale
a) obtiznost prekladu z jednoho jazyka do druheho (ten mensi, i kdyz dost velky problem)
b) server muze napriklad validaci provadet oproti DB, coz javascriptovy klient nemusi mit umozneno. Pravidla na serveru a klientu by pak nebyla stejna, na klientu by zrejme byla jen nejaka oklestena verze serverovych. Toto rozliseni je snad jeste neprijemnejsi nez a)

smilelover

ad 1) Validace se temer vzdy da rozdelit na logiku a pravidla. Delky retezcu, hranice cisel, cisla vyhovujici rovnicim, regularni vyrazy. A jestli se tato pravidla pak pouziji v JS nebo treba v PHP, to uz je fuk.

ad 2) opakovani kodu jsem myslel prave to, ze kdyz rozdelime validaci na pravidla a kod validujici data podle pravidel, bude ten kod pro velky pocet pripadu naprosto stejny.

Validace pomoci anotaci v Jave je naopak velmi navykova :-) Jen je problem, ze hodnota property anotace nemuze byt treba vysledek volani staticke metody, ani kdyz ma RetentionPolicy.RUNTIME.

ad 3) jo, tak pokud jde o pravidla slozitejsi nebo vazana na existujici data, tak je lepsi se na validaci na klientovi uplne vykaslat. Je to zbytecny luxus.

Finta

Z hladiska bezpecnosti je potrebne aby sa data validovali na strane servera. Validacia na strane klienta moze byt riesena cez Ajax. Tym sa da pekne vyhnut duplicite validacneho kodu a uzivatel dostane privetive prostredie. V pripade vypnuteho JS sa formular zvaliduje iba na strane servera.

Sten

V GUI na každou uživatelskou akci jednořádková obsluha. Pokud má funkce button_Click víc než jeden řádek, je to špatně.

Ještě lepší je nedělat explicitní obsluhu vůbec, ale svěřit problém signálům a slotům (aka událostem a delegátům) a napojit je při tvorbě View.

Definovat View jako interface. Ten interface pak musí být možné implementovat např. jako formulář, webovou stránku nebo v konzoli

Nejenom View, ale i Model by měl být interface. Potom můžeme dávat data na různá místa se stále stejnými View a Controllerem.

dc

pretoze vecsina web app/jazykov/platforiem zo zaciatku neobsahovali ziaden framework tak sa znovu objavuje koleso a sme uneseny ze nieco take existuje.Nechcem tym nikoho zhadzovat alebo celu teoriu MVC odpisovat, len mi to pride trochu zvratene.Mam pocit ako keby velka cast web vyvojarov proste zacala rovno z webom a nikdy do incoho ineho ani nepichla a preto su z toho taky uneseny, pritom pri beznych desktopovych aplikaciach toto su v podstate bezne veci.Napriklad take MFC funguje vlastne ako MVC framework (da sa na to aj tak pozerat).

danaketh

Ono to tak ale často skutečně je. Já sice začal na Basicu a Pascalu ale v té době by mi MVC jen komplikovalo život. Teď si od toho webového patlání odskakuji k Pythonu (a občas naťuknu Ruby) a tak MVC a další možnosti poznávám až takhle pozdě.

Anonymní

Tiez mi pride, ze architektura dokument/pohlad v MFC je velmi podobna v clanku opisovanemu MVC. Nie som vsak ziadny odbornik, ale potesilo by ma keby ste mohli, hoci velmi strucne, ukazat v com su si odlisne.

Yenya

Nejprve: diky za clanek, tesim se na dalsi pokracovani. Treba mi mnohe osvetli.

Na druhe strane: je striktni rozdelovani na M, V a C vubec k necemu? Neni jednodussi proste podle potreby pouze refaktorizovat kod (ve stylu "kdyz uz neco delam podruhe/potreti, udelam si na to funkci/objekt/modul"?). Proc to striktne oddelovat? Chapu ze ma smysl oddelovat velkou cast View nekde bokem (uz proto, ze grafickou stranku veci casto navrhuje grafik/webdesigner, coz je nekdo jiny nez programator), ale duvod oddeleni M a C prilis nechapu, zvlast kdyz oboji pise ta stejna skupina lidi.

Ja jsem zatim ziskal pocit, ze MVC jen vytvari taaakhle tluste mezivrstvy, ktere v podstate nic nedelaji.

Videl jsem ne uplne maly (ale asi ne uplne velky) projekt v Ruby on Rails, kde se autor snazil dodrzovat MVC metodologii. Zhruba tretina az polovina kodu bylo zpristupnovani dat z SQL databaze do Ruby objektu (nepochybne znacna cast z toho mohla byt generovana), o neco mene bylo generovani HTML stranek, a uplne nejmene zrejme ten Controller. Akoratze vetsina toho kodu ve vsech trech vrstvach byly triviality typu "vezmu tento objekt/tento SELECT a toto z neho vypisu/vratim". Cili strasne moc textu ktery jen obaloval vrstvy do dalsich vrstev. Bylo tam zoufale malo mist, kde ten kod opravdu neco s daty delal. A kdyz clovek ukazal na stranku a chtel vedet proc je tam tato hodnota nebo co se stane kdyz stisknu toto tlacitko, bylo treba duvod hledat rozstrkany po nekolika oddelenych souborech zdrojoveho kodu.

Neni lepsi napriklad zapouzdrovat SELECT do neceho vetsiho az v pripade, kdy opravdu tentyz SELECT potrebuju vickrat? Tyhlety pokusy o zapouzdreni databaze do objektu vedou pak k tomu, ze nevyuzivam moznosti databaze (abych slozitejsim SELECTem nechal databazi spocitat to co fakt v tomto konkretnim pripade potrebuju), ale pouzivam DB jen jako neinteligentni uloziste, a pak az ex post si z objektu vybiram, co potrebuju.

Diky za pripadnou reakci na vyse uvedene treba v nekterem dalsim dile serialu.

-Yenya, http://www.fi.muni.cz/~kas/blog/

Sten

Důvod oddělení Modelu od Controlleru je kvůli tomu, že Model jsou opravdu jen nějak získaná data, která nemají sama nic dělat. Může to být třeba tenká vrstva nad databází, která bude vybírat data, jak o ně bude Controller (nebo View) žádat, nebo naopak úplně stejné View i Controller můžeme použít v embedded aplikaci, kde Model bude ukládat data do souboru a při startu je načte do paměti. Rozdíl je v tom, že nemusíme sahat do View ani Controlleru, tzn. nemusíme je ani znovu testovat. Stejně tak, pokud někdo přidá nové View a upraví Controller (nebo přidá nový; jeden Model může mít i více Controllerů), tak ho můžeme rovnou použít v obou verzích aplikace.

V test-driven development se naopak běžně nahrazuje Model za pevně daná data a zkouší se, jestli Controller reaguje správně (mění správná data) či View vykresluje to, co požadujeme. Stejně tak můžeme nahradit Controller za něco pevně daného a zkoušet Model. Díky tomu se testují jednotlivé části samostatně, což zjednodušuje ladění i optimalizace a také umožňuje lépe rozdělit práci v týmu (můžete vyvíjet Controller, i když nemáte Model, stačí mít příslušné testy).

Pokud Model jako obal databáze neumožňuje udělat složitější SELECT, který Controller nebo View potřebuje, bude nejspíš chyba v konkrétním Modelu.

Samozřejmě MVC se nehodí na všechny případy, u jednodušších aplikací je to zbytečně složitý koncept.

Sten

MVC docela běžně používám, takže o tomto vím. Ano, ve svém příspěvku jsem to dost zjednodušil, Model může být i hodně komplexní, může obalovat třeba nějaké košaté stromy nad kombinací databáze, souborů a RPC volání. Samozřejmě závisí to na tom, jestli berete Model jako abstraktní interface (potom je úplně nezávislý) nebo jako konkrétní implementaci (tak to beru já, nic o Servisní vrstvě nevím, protože o způsobu uložení dat MVC nic neříká).

Sten

Model jako abstraktní interface není úplně přesně. Nemyslím tím interface ve stylu Javy (tzn. nulová funkčnost), ale interface ve stylu C++ CRTP, kdy interface obsahuje validaci (a další logiku, která je společná pro všechny implementace příslušného Modelu), ale vše ostatní nechává na konkrétní implementaci.

Yenya

Ještě jinak: striktním vyžadováním těchto vrstev se obíráte o značnou škálu prostředků. Kam byste v MVC zařadil třeba triggery, uložené procedury v databázi, anebo třeba databázové zprávy (Oracle: dbms_alert)?

Nebo: lze pomocí MVC udělat aplikaci, ve které uživatel sám v podstatě skládá SELECT z nějakých dodaných prvků? Jako vývojář v zásadě víte nad jakými tabulkami tak ten SELECT může běžet, podle jakých sloupců se spojovat, podle jakých kritérií vybírat a třídit, a jak vypisovat data, ale to je tak všechno. Zbytek si určuje uživatel (příklad: různé statistické aplikace pro manažery – chtějí se na data dívat z fakt různých pohledů). Ne že by uživatel psal přímo SQL, ale v zásadě jeho podobu nějak určuje. Pokud toto není programovací nástroj převést na jeden velký SELECT, nevyužívá možností databáze a nemůže být optimální.

Nevím jestli tohle běžné MVC systémy umožňují (ale určitě je to layering violation, čemuž se snaží zabránit). A přitom je to v mnoha případech dost užitečné. Ne že by takto měl být psaný celý velký systém, ale je potřeba aby se ty mezivrstvy necpaly tam kde je explicitně nepotřebuju.

-Yenya, http://www.fi.muni.cz/~kas/blog/

Sten

Triggery patří do Modelu, stejně jako uložené procedury. Databázové zprávy by asi také patřily do Modelu (a Controller by si je vyzvedával), ale tam si nejsem jistý.

Pokud by se vhodně navrhl Model, tak by to šlo, ten SELECT by ale samozřejmě musel sestavit (a optimalizovat) on. Ale v tomhle případě by MVC asi bylo spíš na obtíž.

Víko

Záleží na tom, co je v uložených procedurách implementováno. Rozhodně se nedá paušálně řící, že nemají s MVC nic společného.

YF

MVC je architektonicky vzor primarne urceny pro systemy implementovane pomoci OOP – dal si nemyslim ze by vrstvy byly zlo :) obecne jsou vrstvy jenom jednou z mnoha forem dekompozice … MVC je rekl bych surovy zaklad vhodny pro systemy kde je jiz zapotrebi premyslet o napriklad rozsiritelnosti a nebo napriklad pokud mate tym lidi a projekt na kterem potrebujete resit rozdeleni prace – neni to dogma a jako vse se i toto da zneuzit nebo nespravne pouzit

Anonymní

Kontroler je uzitecny na hookovani kodu, oddeleni kontroleru a modelu se velmi hodi pro snadne testovani, ale i pro CLI utility pracujici s aplikaci. Rozhodne si nemyslim ze by ten pattern byl zbytecny :)

Yenya

Když jejich zodpovědnosti namícháte, stane se vám například, že Controller bude obsahovat doménovou logiku a tu budete muset duplikovat, když bude Controllerů víc. Zde mi asi namítnete, že v tu chvíli můžete doménovou logiku extrahovat do nějakého jiného objektu a že tím duplikaci zabráníte, ale víte co? V tu chvíli vytváříte doménový model, respektive jeho část!

Samozřejmě, ale opravdu vytvářím jen část, a to přesně tu část, kterou tam mít musím (jinak bych duplikoval kód), a přesně v té chvíli, kdy ji začnu potřebovat. Ne dřív. Já netvrdím, že MVC-like architektura je obecně zlo, jen tvrdím, že mezivrstvy (raději ale volitelně použité helpery) by se neměly psát předčasně.

-Yenya

v6ak

Řekněme, že mám nějaké políčko na sumu peněz. Bude to tedy desetinné číslo (třeba BigDecimal). Podmínky budou následující:
1) z definice vyplývá, že musí jít o číslo
2) z nějakého dalšího důvodu to musí být 200 – 500
Pravidlo č. 2 patří jednoznačně do modelu. Pravidlo č. 1 tam ale jednoznačně nepatří: model to dostává jako BigDecimal => ani se to k němu nemá jak dostat.

ava

Souhlas, 1) jsem povazoval za samozrejmost (patri s nejvyssi pravdepodobnosti do controlleru), mluvil jsem o validaci business logiky :)

v6ak

Na tom, jestli jde o validaci v modelu, se shodneme, jen to říkáme jinak.
Samozřejmě, mohou tu být i omezené inputy, ale na nich to neukážu. A netvrdím, že to tam musí být, jen že za určitých podmínek můžu narazit na validaci mimo model. Prostě doufám, že se nenajde nikdo, kdo bude chtít třeba setMoney(String) /* první, co mě napadlo */ s validací čísla s tím, že veškeré validace mají být přece v Modelu.

dave

Tak jak to chápu já, tak v modelu musí být vždy všechna validační pravidla. To je jestli je implementuje i view je pak v podstatě jedno. Model je musí mít vždy.
Jak jinak by pak šlo snadno nahradit jeden view druhým? Např. webové rozhraní, klasickým UI nějakého lokálního klienta.

Takže v tomto případě, model si zkontroluje zda dostal bigdecimal a pak si zkontroluje jestli je mezi 200 a 500.

David Grudl

Výborně napsané a hlavně jsem rád, že tenhle seriál vůbec vznikl.

jos

Někomu se asi při četbě následujícího nebude zdát výskyt slova Struts. Jenže MVC je pořád stejná m*dka bez ohledu na jazyk, sebelepší implementace to nezmění.

Allen Holub: Holub on Patterns:

Returning to Struts, this library isn't a model of OO architecture and was never inteded to be. The MVC architecture embodied in Struts pretty much forces you to use get/set methods. You can reasonably argue, that given the generic nature of Struts, it can't be fully OO, but other UI architectures manage to hide encapsulation better than MVC. Perhaps the real solution is to avoid an MVC based framework altogether. MVC was develpoed almost 30 years ago, and we've learned a lot since then.

jos

to není extrámní názor, to je oprávněná kritika ;) nikoliv však článku (i o špatnejch věcech se může psát, že), ale architektury MVC

T

V prvom rade vdaka za clanok. Vdaka bubline okolo ASP.NET MVC sa dostava do popredia problematika MVC aj v (ASP).NET komunite. Casto chyba ale background, ktory tento clanok poskytuje

K uvodnej casti clanku nemam vyhrady, zial, ten zaver(posledny odsek) mi pride ako trosku zmotany, bud prilis vela nedotiahnutych myslienok na kope, alebo nedoslednost.

1. "Toto jsou také komponenty, které se v jednotlivých variacích liší, zatímco Model je stále stejný, a proto k němu můžu uvést pár poznámek už tady"

bohuzial, aj model moze byt ponaty rozne – aktivny(povedzme povodny koncept v duchu ktoreho su aj schemy v uvode clanku) a pasivny model, co ma potom zasadny dopad na riesenie controllera a view.

2. "Model ve smyslu MVC je však daleko víc, je to tzv. doménový model"

Model v zmysle MVC moze byt aj anemicky model(nechapat nutne ako urazku)(=transaction script pristup), active record, rozvinuty DDD model (ano, s domain modelom na vrchu), mozu to byt hoci vec services…od tohoto MVC ako pattern abstrahuje(vid. napr. GoF), nie je to vobec podstatne. (je tu vsak samozrejme otazka, ako riesit v kontexte konkretnej zvolenej architektury aktivny model, ak chceme ist touto cestou ;-)

"doménový model, který modeluje vztahy reálného světa"

bohuzial, toto je dost zavadzajuce zjednodusenie pointy DDD.

3. "např. validátory v ASP.NET nebo ve Flexu), v architektuře MVC však správně patří do Modelu."

Tu uz naozaj zachadzame do DDD. Nesuhlasim, zase nieco, co navrhovy vzor MVC neriesi.(vid. GoF)

Navyse, okolo tohoto je neuzavreta diskusia aj v ramci DDD sceny. Business rules ok, v idealnom pripade maju byt v domain vrstve, okolo toho je zhoda. Ale co so zakladnymi validaciami inputov (ktore maju mimochodom ambiciu riesit asp.net validatory?). Aby som mohol validovat v business vrstve, musim mat aspon vstupy v nejakej podobe, ktore jej dokazem posunut.

4. "V klasickém třívrstvém modelu, kde máme datovou, aplikační a prezentační vrstvu, Model v prezentační vrstvě v principu odpovídá modelu v aplikační vrstvě. Bohužel není zcela obvyklé, aby nástroje uměly tyto modely synchronizovat – tento úkol je tak většinou na bedrech programátora."

Prax ukazala, ze Views musia riesit aj otazku stavu GUI / document modelu / modelu prezentacnej vrstvy.(pocnuc SmallTalkom) a ze je z hladiska praxe rozumne uvazovat o dvoch veciach(co v pripade predhistorickych uvah o GUI bolo bezpredmetne). Z tohto faktu sa vyprofiloval Fowlerovsky MVP, ktory ponuka pekne riesenie/vychodisko.

Vid. materialy napr. na Fowlerovej stranke ku tejto problematike.

T

Dakujem aj ja za reakciu. Myslim, ze nazorovo mame blizko(co ma tesi) a v podstate ide +- o hru so slovickami, netreba preto v mojich nasledujucich poznamkach hladat nutne oponovanie ;-)

– Fowler napr. v PoEAA popisuje rozne (enterprise) modely. DDD pristup sa hodi len na rozsiahle projekty s velmi zlozitym modelom, to zdoraznuje Fowler aj Evans(aj ked mnohe principy je mozne aplikovavat samozrejme na mensich). Netreba tento rozmer zbytocne vnasat do problematiky MVC, to je len nazor. Aj ked tomu oznaceniu dava Fowler v clanku, ktory sme citali asi obaja negativny nadych, v kontexte konktretneho projektu a jeho specifik(maly, CRUD oriented) by som to tak nevidel.
Pod anemickym ma na mysli predovsetkym odnatie behavior z domenovych objektov.
Ludia stavajuci napr. nad active record domenovy model v podstate nemaju, spliva s datovym modelom, alebo ho maju nad active recordom ako transaction scripty.

– Validacne pravidla / business rules, to sa zhodneme, patria do modelu, netreba to rozpitvavat.(ako z modelu passnut vysledky validacii a sprostredkovat ich pouzivatelovi, je temou na dlhe zimne vecery :-)
Otazne je, kam patria zakladne validacie user inputu(napr. datovy typ – date, int), aby bolo mozne vobec posunut input do domain modelu, upozornoval som na toto(a tusim aj iny citatelia vyssie)
Dalej je otazne ako riesit zobrazenie mandatory flagu pri inpute, ak povinnost vyplnit pole je zavisla od nejakeho business pravidla a pod.
ASP.NET validatory je mozne pouzit len na takuto zakladnu validaciu, hoci sa bezne pouziva aj na komplexne validacie ktore spadaju uz pod business.
(Dovolim si povedat, ze prave MVP dava na pekne odpovede na tieto problemy ovela viac priestoru)

– Modelovanie vztahov realneho sveta – ano, rozumiem, ta veta vo mne evokovala taku stratenu vetvu OOD, len som mal potrebu sa uistit – rozmyslam, ako to formulovat lepsie… ?? Domain model z hladiska DDD = vyber takej abstrakcie (takeho modelu), ktora najlepsie popisuje zakaznikov pohlad na svet(problemovu domenu), ktora je teda mysleniu zakaznika najblizsia. Je to podriadenie sa videniu sveta zakaznikom(resp. teami zakaznikovych domain expertov).

– MVC ma/nema riesit. MVC je pattern. Pattern je (overenou) odpovedou na urcity problem, ktory ma ambiciu riesit. To ako (konkretne) designovat model je predmetom inych patternov.(ktore rozobera ako bolo spomenute komplexne napr. Fowler). Uz takto je MVC v pozicii super patternu (niecoho co stavia na inych patternoch)

———-
– Aktivny model – vytvara notifikacie o zmenach, ktore sa v nom udiali. Views na tie, ktore su pre ne zaujimave pocuvaju, reaguju na ne tak, ze oslovia priamo model, ziskaju potrebne data a zobrazia ich/premietnu zmeny v modely na screen.

– Pasivny model napr. pri ASP.NET MVC. Model nevytvara notifikacie o tom, ze sa v nom nieco zmenilo. View sa musi o tom, ze sa ma prerenderovat dozvediet inak. Typicky tak, ze controller posle message(view potom priamo accessuje model) alebo(a to je menej stastne podla mna) ako je to v ASP.NET MVC, ze controller natlaci data(projekciu dat z modelu) do view a forcene jeho prerenderovanie a pod.

popisane namatkovo tu
http://www.phpwact.org/pattern/model_view_controller

T

Je to tak ;-)

Anonymní

Rozumiem akurat "accessuje", "forcene" a "prerenderovanie". Zbytok je aky jazyk?

YF

neuveritelny jak se da z jednoduche myslenky vyrobit neco tak radoby sofistikovaneho – clanek mi spis pripada jako navod jak si postavit raketoplan nez popis vzoru – a to jeste autor slibuje pokracovani (ceho? :)) zda se mi ze se tu porad nekdo snazi prevykladat tu samou vec originalnim zpusobem ale uplne se zapomina na vlastni ucel toho co by vzor mel plnit …
spojovat jednotlive vrstvy pseudosemantikou v podobe car a rikat jim "(ne)prime vazby" atd. to mi prijde uz silna kava :) ale tak chybama se clovek uci … tak preju at to nejak de aspon kdyz uz nic :)

YF

"architektura MVC" je proste blabol – MVC je vzor – relativne jednoducha myslenka (kterou architektura nejakym zpusobem zahrnuje) pojmenovavat architekturu podle vzoru a popisovat ji timto zpusobem zvlast je redundantni – pak to vypada jako raketoplan :) na architekturu je kladeno daleko vic pozadavku a MVC adresuje jen nektere z nich

YF

kratce? :) no to uz bude na case! :))

weeto

A neměla by toto řešit hned první kapitola prvního článku?

Marián Košťál

Prilis nesuhlasim s nazormi v odstavci "Motivace aneb problémy drag & drop vývoje". Myslim, ze je dolezite uvedomit si, cim sa odlisuju komponentove frameworky (ASP.NET WebForms) a poziadavkami riadene frameworky (ASP.NET MVC). Moj nazor je tu http://nmarian.blogspot.com/2009/01/porovnanie-aspnet-webforms-aspnet-mvc.html. Samotny vzor MVC je velmi stary (1979) v sucastnosti vsak jeho popularita rastie. Kazdy z typov webovych frameworkov v roznej miere naplna atributy kvality (udrziavatelnost, testovatelnost, znovupouzitelnost, ….) je to iba otazka volby pre co sa rozhodneme a ktore atributy kvality su pre nas dolezite. Prax myslim ukazala, ze takisto ako sa daju implementovat aplikacie pomocou komponentovych frameworkov, tak sa to da aj pomocou poziadavkami riadenych frameworkov.

ee

ja nevim co programujete, ale ten diagram je totalne spatne!!!! model logicky musi ovlivnit view tedy vazba mezi modelem a view je nezbytna!!!!!

Martin Hassman

Já nevím, co komentujete, ale ten komentář je totálně špatně!!! Čtenář si musí napřed přečíst celý článek, aby ho správně pochopil a nedělal pak ze sebe v komentářích vola. Tato vazba dost důležitá. I v životě 8-)

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.