Curs 11 Modele Arhitecturale Care Asigur Separarea
11.1 Model-View-Controller
Model-View-Controller (MVC) este un model arhitectural care separ funcionalitatea specific domeniului pentru care este dezvoltat sistemul software de interfaa grafic a aplicaiei, permind dezvoltarea, ntreinerea i testare separat a celor dou pri. n Figur1 este descris grafic modelul arhitectural MVC. Acest model mparte un sistem software n trei pri, i anume: controller, view i model. Modelul gestioneaz datele sistemului software, rspunde la interogri referitoare la stare (de obicei solicitate de View) i realizeaz operaii de modificare a datelor (de obicei invocate de controller). n cazul sistemelor bazate pe evenimente, modelul notific observatorii (de obicei view-urile), atunci cnd informaia se modific pentru ca acetia s poat reaciona la aceste modificri. View-ul red modelul ntr-o form care permite interaciunea cu utilizatorul, de obicei prin intermediul elementelor de interfa cu utilizatorul. Pentru un singur model pot exista mai multe view-uri pentru a deservi diferite scopuri. Controller-ul recepioneaz aciunile utilizatorului i rspunde interognd modelul.
Un sistem software care implementeaz modelul arhitectural MVC poate fi vzut ca i o colecie de triplete (model, view, controller). Fiecare triplet este responsabil de un anumit element al interfeei grafice. Acest model arhitectural este ntlnit att n sisteme software de tip aplicaie desktop ct i n cele de tip aplicaie web. Sistemul Swing de interfee grafice este o tehnologie Java care modeleaz aproape toate componentele interfeei grafice ca i sisteme MVC individuale. Ca exemplu de tehnologie Controller View Model eveniment din interfaa grafic eveniment modificare model Figur1: Model-View-Controller
2 web care folosete modelul arhitectural MVC se poate considera ASP.NET MVC. n cazul ASP.NET MVC modelul poate fi implementat prin clase .Net care modeleaz structurile de date (de ex.: se poate folosi Microsoft Entity Framework), controller-ul trebuie derivat dintr-o clas special (Controller), iar view- urile sunt implementate sub forma unor fiiere care conin un amestec de cod C# i HTML (Razor view engine). n continuare este prezentat un exemplu de pagin web implementat n ASP.NET MVC (Figur2). Controller pentru aceast pagin este prezentat n Cod 1. Controller-ul conine metoda LogOn suprancrcat: varianta fr parametrii este invocat atunci cnd un client face o cerere de tip GET (utilizatorul a scris n bara de adrese adresa pentru pagina de LogOn), iar varianta cu doi parametrii (model, returnUrl) este invocat atunci cnd un client trimite o cerere de tip POST (utilizatorul a dat click pe butonul de Log On). Testarea controller-ului se poate face foarte uor crend un proiect de test i invocnd metodele din controller. Implementarea view-ului, utiliznd engin-ul Razor, este prezentat n Cod 2. View-ul nu face alt ceva dect s permit vizualizarea i editarea informaiei din model. Model din spatele paginii este prezentat n Cod 3, acesta conine proprieti pentru fiecare cmp de pe ecran, n plus proprietile sunt adnotate cu atribute care descriu validrile ce trebuie s se execute asupra acelor cmpuri.
Figur2: Pagin ASP.NET implementat folosind ASP.NET MVC publicclassAccountController : Controller {
// Method invoked when a client performs a GET operation on // the url http://<site>/Account/LogOn
3
publicActionResult LogOn() { return View();//returning view will redirect to the LogOn page, action performed automatically //by the MVC system }
//Method Invoked when user dose a POST back on the LogOn page, e.g., hits Log On button // POST: /Account/LogOn [HttpPost] publicActionResult LogOn(LogOnModel model, string returnUrl) { if (ModelState.IsValid) { if (Membership.ValidateUser(model.UserName, model.Password)) { FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe); if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/") && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\")) { //redirect to the specified URL return Redirect(returnUrl); } else { //go to home page return RedirectToAction("Index", "Home"); } } else { //report login error ModelState.AddModelError("", "The user name or password provided is incorrect."); } }
// If we got this far, something failed, redisplay form return View(model); } } Cod 1: Exemplu de Controller ASP.NET MVC (sursa: Microsoft Visual Studio Template)
@model SampleMVC.Models.LogOnModel
@{ ViewBag.Title = "Log On"; }
<h2>Log On</h2> <p> Please enter your user name and password. @Html.ActionLink("Register", "Register") if you don't have an account. </p>
[Display(Name = "Remember me?")] publicbool RememberMe { get; set; } } Cod 3: Exemplu de model ASP.NET MVC (sursa: Microsoft Visual Studio Template)
11.2 Pasive View
Modelul arhitectural Pasive-View (Figur3) este derivat din modelul Model-View-Controller, diferena major const n faptul c view-ul este folosit doar pentru prezentarea informaiei, n rest el fiind pasiv, iar toate cererile utilizatorului sunt preluate de ctre controller. n plus view-ul nu se mai actualizeaz automat direct din model, astfel nu mai exist o dependen ntre model i view. Principalul motiv pentru care se folosete modelul Pasive-View este posibilitatea de a implementa unit testing. Astfel avnd n vedere c view-ul este pasiv este suficient s se testeze doar controler-ul fr ns a fi nevoie de interaciunea cu interfaa grafic, ceea ce nseamn c testele pot fi automatizate cu uurin.
5
11.3 Model-View-ViewModel
Model-View-ViewModel (MVVM) este un alt model arhitectural derivat din MVC. La fel ca i n cazul modelului Pasive-View nu exist o dependen ntre view i model, dar spre deosebire de acesta view-ul nu este pasiv ci poate actualiza controller-ul care n acest caz este reprezentat de ViewModel. Cu toate acestea din punct de vedere al testrii automate doar ViewModel-ul trebuie testat, ntruct comunicarea ntre View i ViewModel se face prin data binding, care cel puin teoretic nu poate conine erori de logic. Modelul arhitectural Model-View-ViewModel este un model specific platformelor .Net: Windows Presentation Foundation i Silverlight. Pentru a uura dezvoltarea aplicaiilor MVVM au fost dezvoltate o serie de toolkit-uri care faciliteaz implementarea acestui model arhitectural. Unul din cele mai cunoscute astfel de toolkit-uri este MVVM Light Toolkit [2].
n Figur5 este prezentat o pagin Silverlight care a fost implementat utiliznd modelul arhitectural MVVM; pentru implementarea modelului MVVM s-a folosit toolkit-ul Mvvmlight. ViewModel-ul este prezentat n Cod 4. ViewModel-ul este reprezentat de o clas derivat din clasa ViewModelBase. Pentru fiecare control din View, ViewModel-ul conine cte o proprietate, pe care se face binding n View. Astfel pentru exemplul discutat exist trei proprieti: UserName, Passowrd i LoginCommand. Proprietile UserName i Password sunt de tipul text, n plus se poate observa c setarea unei valori n aceste proprieti se face prin intermediul funciei Set care pe lng setarea valori va genera un eveniment care va notifica View-ul c proprietatea s-a modificat. Proprietatea LoginCommand este mai special pentru c ea corespunde unui buton (butonul Login), astfel aceast proprietate este de tipul RelayCommand i Controller View Model eveniment din interfaa grafic data binding ViewModel View Model Figur3: Pasive-View Figur4: Model-View-ViewModel
6 permite asocierea unei funcii care s fie invocat atunci cnd butonul Login este apsat. ViewModel-ul conine i o referin la model (_dataService) prin intermediul creia poate interoga i modifica modelul (atunci cnd este apsat butonul Login se invoc metoda Authenticate din model). ntruct ViewModel- ul nu conine o referin la View pentru a notifica View-ul s navigheze la alt pagin sau s afieze un mesaj se folosete sistemul de mesaje disponibil n Mvvmlight. n exemplul din Cod 4 ViewModel-ul trimite un mesaj View-ului atunci cnd are loc autentificare (MessengerInstance.Send(autResult)).
Figur5: Aplicaie Silverlight pentru ilustrarea modelului arhitectural MVVM ///<summary>Class that implements view model for the login view.</summary> publicclassLoginViewModel : ViewModelBase { publicconststring UserNamePropertyName = "UserName"; publicconststring PasswordPropertyName = "Password";
privatestring _userName = ""; privatestring _password = ""; privateRelayCommand _loginCommand; ///<summary> Reference to the service that can be used to access the model. </summary> privateIDataService _dataService;
///<summary> /// Sets and gets the UserName property. /// Changes to that property's value raise the PropertyChanged event. ///</summary> publicstring UserName { get{return _userName;} set{Set(UserNamePropertyName, ref _userName, value);} }
///<summary> /// Sets and gets the Password property. /// Changes to that property's value raise the PropertyChanged event. ///</summary> publicstring Password { get{return _password;} set{Set(PasswordPropertyName, ref _password, value);} }
///<summary> /// Gets the LoginCommand. This command is invoked when user clicks login. ///</summary> publicRelayCommand LoginCommand {
///<summary>Initializes a new instance of the LoginModel class.</summary> public LoginViewModel(IDataService dataService) { _dataService = dataService; } } Cod 4: Exemplu de ViewModel pentru o aplicaie Silverlight. Pentru a facilita accesul la ViewModel-uri n View-uri se folosete o clas denumit locator (Cod 5), care va conine referine statice la toate modelele din aplicaie, n plus tot aceast clas este responsabil cu instanierea modelului i a ViewModel-urilor.
///<summary> /// This class contains static references to all the view models in the /// application and provides an entry point for the bindings. ///<para> /// Use the <strong>mvvmlocatorproperty</strong> snippet to add ViewModels /// to this locator. ///</para> ///<para> /// See http://www.galasoft.ch/mvvm/getstarted ///</para> ///</summary> publicclassViewModelLocator { static ViewModelLocator() { ServiceLocator.SetLocatorProvider(() =>SimpleIoc.Default);
///<summary> /// Gets the Login property. ///</summary> [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This non-static member is needed for data binding purposes.")] publicLoginViewModel Login { get { returnServiceLocator.Current.GetInstance<LoginViewModel>(); }
8 } } Cod 5: Clasa locator folosit pentru a face legturantre ViewModel i View View-ul este prezentat n Cod 6 i Cod 7. Legtura dintre ViewModel i View se face exclusiv prin data binding. Astfel proprietatea DataContext este legat la proprietatea Login din clasa locator, care reprezint o instan a ViewModel-ului. Fiecare control este legat de cte o proprietate din ViewModel: TextBox-ul pentru introducerea numelui utilizator este legat de proprietatea UserName, TextBox-ul pentru introducerea parolei este legat de proprietatea Password, iar butonul Login este legat de comanda LoginCommand. n clasa din spatele View-ului exist foarte puin cod, doar se intercepteaz mesajele primite din ViewModel i se afieaz mesaje pentru utilizator.
</Grid> </UserControl> Cod 6: Exemplu de View pentru o aplicaie Silverlight ///<summary> /// Description for LoginView. ///</summary>
9 publicpartialclassLoginView : UserControl { ///<summary> /// Initializes a new instance of the LoginView class. ///</summary> public LoginView() { InitializeComponent();
Messenger.Default.Register<LoginDataItem>(this, (autResult) => { if (autResult.LoginSuccessul) MessageBox.Show("User successfully authenticated."); elseMessageBox.Show("User authentication faild."); }); } } Cod 7: Exemplu de code-behind pentru o aplicaie Silverlight Pentru reprezentarea modelului (Cod 8) s-a definit mai nti o interfa (IDataService) pentru a se putea comuta cu uurin ntre diferite tipuri de modele. Fiind vorba de o aplicaie Silverlight operaiile de interogate i modificare a modelului de date sunt operaii asincrone pentru a evita blocarea aplicaiei atunci cnd se ateapt un rspuns de la serviciul de date. publicinterfaceIDataService { void Authenticate(string userName, string password, Action<LoginDataItem, Exception> callback); }
publicclassLoginDataItem { public LoginDataItem(bool loginSuccessful) { LoginSuccessul = loginSuccessful; } publicbool LoginSuccessul { get; set; } } Cod 8: Exemplu de model pentru o aplicaie Silverlight