Používáme Model-View-ViewModel – úvod
Začínat s vývojem jakékoliv prezentační vrstvy vede k tomu, že použijeme některý z okoukaných postupů, které zahlédneme na prezentacích nebo konferencích. Tyto postupy jsou však vhodné právě pro tyto akce, pokud chceme uživatelské rozhraní dále rozšiřovat, pracovat s ním v o něco složitejším projektu, je vždy vhodné použít některý z návrhových vzorů pro prezentační vrstvu.
Jak to je u WPF a Silverlight aplikací
Když začínáme aplikace, a je jedno zda se jedná o WPF nebo Silverlight aplikaci, a nemáme téměř žádné zkušenosti, nejspíše sklouzneme k tomu, že použijeme některý z postupů, který jsme použili již dříve a to při tvorbě WinForm nebo ASP.NET aplikace. Takže po deklaraci XAML stromu elementů dojdeme k tomu že:
- jednotlivé elementy pojmenujeme pomocí x:Name
- implementujeme handlery, nebo si alespoň v první fázy necháme vygenerovat šablony pro jejich implementaci
- v code behindu okna si uložíme referenci na model
- přímo naplníme pojmenované elementy, případně z nich přečteme hodnoty
Jedná se o asi nejsnazší způsob, jak vytvořit první WPF nebo Silverlight aplikaci a samozřejmě nám to nemůže mít nikdo za zlé. Ono se také jedná o asi nejrychlejší způsob, jak dosáhnout u malé aplikace požadovaného cíle – i proto se tento postup tak často ukazuje na konferencích a v prezentacích.
Nevýhody tohoto přístupu
Jak asi tušíte, když jsem se o tomto tématu rozepsal, tento přístu bude mít hned několik nevýhod, které se pokusím shrnout a maličko okomentovat.
- View (XAML) a code behind jsou silně svázány – co to znamená je, že v kódu na pozadí daného okna se přímo odkazuji na jednotlivé pojmenované elementy v deklaraci xaml. Kód tak nemůže existovat sám o sobě a je tak svázán s deklarací (druhou částí partial třídy)
- View se stává úložištěm dat místo aby je jen zobrazovalo – to se především týká třetího, výše zmíněného, bodu – vytvoření reference na model, kdy v code behind přímo komunikujeme s modelem. Přitom View by se mělo starat, pokud možno, pouze o zobrazování dat.
- Kód na pozadí View není možné snadno testovat – tím, že v code behind přistupujeme přímo na pojmenované prvky ve View se dostáváme do nepříjemné situace, kdy není možné snadno otestovat tento kód na pozadí. Souvisí to ze zde zmíněnou první nevýhodou, ale také s druhou, kdy code behind referencuje model a ten je hůře nahraditelný při unit testování.
- Poslední velkou nevýhodou je to, že nevyužíváme silných možností oboustranného DataBindingu
Možnost k nápravě – návrhové vzory
Pokud jste alespoň maličko zkušenějšími vývojáři, jistě jste slyšeli o tom, že ve vývoji software se často používají návrhové vzory. Pro vývoj prezentační vrstvy je pak definováno několik návrhových vzorů a v poslední době hodně používaný a diskutovaný vzor MVC (Model View Controller). Myslím, že nemá význam zde tento vzor blíže rozebírat a odkáži vás na český zdroj sepsaný Borkem Bernardem [úvod do architektury MVC], [prezentační vzory z rodiny MVC], [alternativy k MVC a závěrečné poznámky].
Jestliže jste vyvíjeli aplikace jak pomocí WinForms nebo ASP.NET, jistě jste se mohli setkat s tím, že jste použili některý z prezentačních vzorů, které vycházejí z principu MVC, nejčastěji to mohl být vzor MVP (Model View Presenter). Při použití tohoto vzoru dojdeme k tomu, že View implementuje definovaný interface a poskytuje tak metody nebo vlastnosti pro nastavení prvků UI. Právě díky implementaci interface zůstane code behind poměrně “čistý” a zároveň je Presenter odpojen od specifik dané prezentační vrstvou, čímž využijeme testovatelnosti takového Presenteru.
Vzor MVP bychom mohli implementovat i při vývoji WPF/Silverlight aplikace, ale:
- nevyužijeme tím plně výhod obousměrného databindingu
- navíc potřebujeme implementovat vlastnosti pro přístup k UI prvkům a zapsat kód pro vyvolání handlerů
Představení Model-View-ViewModel
Pomalu se dostáváme k teoretickému závěru a samotnému představení vzoru Model-View-ViewModel. Tento vzor vychází z principů vzoru MVC a navrhnul jej architekt WPF/Silverlight John Gossman.
Asi hned na úvod mohu říct jednu pozitivní zkušenost a praktickou ukázku použití tohoto vzoru, neboť kompletně celé prostředí Microsoft Expression Blend je napsáno s využitím MVVM vzoru.
MVVM – ViewModel
Jak bychom si lépe mohli představit vzor MVVM než představením toho, co vlastně znamená ViewModel. Ten jediný se maličko liší svým chováním a zodpovědností. View a Model nám zůstávají v tomto vzoru stále stejné, možná s tím rozdílem, že View využívá možnosti, které nabízí WPF/Silverlight a to především zmiňovaný obousměrný databinding.
Vraťme se tedy k ViewModelu a řekněme si, že ten
- představuje jakéhosi prostředníka, který adaptuje Model pro potřeby View.
- je zodpovědný za stav View avšak bez nutnosti přímého zjišťování si hodnot jednotlivých prvků ve View.
- volá model nebo služby přes jejich interface
- vystavuje veřejné vlastnosti, které jsou v XAML bindovány. Aby mohlo docházet ke komunikaci mezi View a ViewModelem, je vhodné aby ViewModelem tyto veřejné vlastnosti byly publikovány jako notifikační. Znamená to tedy, že
- jednoduché typy jsou vystaveny pomocí vlastností typu DependencyProperty, případně je na nich implementován interface INotifyPropertyChanged
- kolekce prvků jsou vystaveny pomocí ObservableCollection<T> nebo jejích potomků
- pro příkazy, které vedou na volání metod modelu/služeb jsou zpřístupněny commandy přes implementované rozhraní ICommand
Cíl použití MVVM
Cílem, který nás vede k tomu, že použijeme a budeme vytvářet aplikace za pomoci vzoru Model-View-ViewModel má být
- snaha o co nejčistější code behind – toto neberte příliš dogmaticky, jsou situace, kdy je třeba v kódu na pozadí vykonat obslužné činnosti a snažit se o použití tohoto vzoru všude může vést spíše k znepřehlednění vývoje
- přenechání odpovědnosti za vytvoření ViewModelu v code behindu View. Opět jsme v situaci, kdy takovýto přístup nemusí být vhodný a existují různé možnosti, jak View a ViewModel společně propojit a určitě si alespoň některé z nich ukážeme v některém z následujících článků
Jsme na dobré cestě, pokud se vyvarujeme použití pojmenování elementů pomocí x:Name. Zde je opět nutné připomenout, že jsou situace, kdy pojmenovat prvek musíme, například když jej chceme referencovat a bindovat v deklaraci xAML.
Závěrem
V tomto článku jsem chtěl představit možnosti, které máme při vývoji WPF/Silverlight aplikace a naznačit postup, jak tvořit lépe spravovatelné, snáze testovatelné a udržovatelné aplikace a to s využitím vzoru Model-View-ViewModel. V dalších článcích bych pak rád představil tento prezentační návrhový vzor blíže a rozebral jeho možnosti.
Komentáře