Orice programator încearcă să fie cât mai ordonat atunci când scrie cod. Fiecare echipă își organizează proiectul în așa fel încât acesta să fie cât mai ușor de întreținut și să poată fi înțeles cu ușurință de membrii noi ai echipei. Această practică poate fi datorată și folosirii design pattern-urilor. Știm că există numeroase astfel de reguli de organizare a codului și că odată aleasă una dintre ele, este greu și costisitor ca aceasta să fie schimbată. Totuși este important ca programatorii să fie la curent cu noile design pattern-uri pentru a putea profita la maxim de avantajele oferite de acestea.
Model-View-Viewmodel (MVVM) este un design pattern folosit în ingineria software care a fost introdus prima oară de către Microsoft ca o metodă de particularizare a modelului de prezentare introdus de Martin Fowler. De fapt se pare că Microsoft folosea MVVM pentru proiectele dezvoltate intern, precum Microsoft Expression Blend, pe când nucleul platformei WPF era în construcție. Bazat în mare parte pe modelul Model-View-Controller (MVC), acesta se adresează în special dezvoltării interfeței cu utilizatorii a platformelor moderne ((HTML5, Windows Presentation Foundation, - WPF, și Silverlight), acolo unde există un dezvoltator orientat special spre acest lucru, având sarcini diferite de ale unui dezvoltator obișnuit care se ocupă în general de logica business și de dezvoltarea interacțiunii cu serverul.
Ca structură, acesta este alcatuit din trei părți esențiale ce pot fi deduse și din denumirea design pattern-ului: un Model, un View și un ViewModel. Această structură seamană cu cea a MVC-ului, dar oferă în plus ușurința utilizării XAML-ului și a Windows Presentation Foundation, prin cuplarea datelor cât mai aproape de Model folosind XAML, View Model și orice verificare de date a nivelului Business pentru a valida datele înaintea afișării acestora pe interfață. Modelul se referă la datele efectiv cu care se lucrează dar și la nivelul de acces la aceste date. Spre exemplu într-un model ar putea fi accesate obiecte care citesc din baza de date informații legate de o anumită persoană. View-urile, la fel ca și în cazul clasic se referă la partea vizuală care va fi afișată pe interfața grafică, cum ar fi butoanele, ferestrele, graficele și alte controale. Acestea nu conțin partea de logică business. Marele avantaj în acest caz este că un designer poate să se ocupe de partea grafică a aplicației lucrând doar cu view-ul, în timp ce logica din spate rămâne neafectată. ViewModel-urile reprezintă niște modele pentru view-uri, mai precis acestea se referă la o abstractizare a view-urilor care servesc și la binding-ul datelor între view și model. Pot fi privite și ca niște aspecte specializate ale Controalelor din design pattern-ul MVC (care acționează ca și data bindgins sau converters) în așa fel încât să schimbe informația din formatul modelului în formatul view-ului și să paseze comenzi din view în model. ViewModel-urile expun proprietățile publice, comenzile și abstractizările și au fost asemănate cu o stare conceptuală a datelor, spre depsebire de starea reală a datelor din model. Există discuții în ceea ce privește clasa din spatele view-ului. Majoritatea specialiștilor susțin că aceasta ar trebui să conțină doar metoda InitializeComponent() în cazul WPF și Silverlight, însă în anumite cazuri nu se merită mutarea unor metode în viewmodel.
View-ul comunică doar cu ViewModel-ul, în timp ce ViewModel-ul este privit ca un punct intermediar între View și Model. De asemenea, Modelul este singurul care interacționează cu baza de date. Acest model are sens în practică doar dacă se folosește în combinație cu o bază de date. În caz contrar obiectele de date precum entitățile din EDMX și Linq nu au logică în acest context. O diagramă a acestui design pattern poate fi observată și in figura de mai jos.
Alte două funcționalități care fac ca acesta să fie atât de des folosit sunt data template-urile și resursele de sistem. Template-urile aplică View-uri asupra obiectelor unui ViewModel. Programatorii pot să le declare în XAML și să lase resursele de sistem să localizeze în mod automat iar apoi să aplice acele template-uri la runtime.
Întrucât aceast design pattern poate fi destul de greu de implementat, au fost create câteva platforme care să ajute programatorii în implementarea lui cum ar fi MVVM Light sau Caliburn. Personal îl recomand pe cel din urmă deoarece oferă avantajul recunoașterii automate a view-ului de către viewmodel, nemaifiind necesară crearea unei clase ajutătoare care să facă acest lucru.
Să luăm un exemplu simplu cu o aplicație care afișează dintr-o bază de date informații despre produse : nume, preț unitar și id. În model va trebui să avem partea de accesare a datelor. Modul în care acestea vor fi accesate rămâne la latitudinea programatorului. Această clasă va implementa INotifyPropertyChanged și va conține cele 3 proprietăți, fiecare având pe setter OnPropertyChanged("Proprietate") pentru a semnaliza modificarea valorii proprietății. Trebuie acordată atenție în mod special numelui proprietății deoarece acesta se transmite ca și string.
ViewModel-ul va conține toată partea de care are nevoie utilizatorul pentru a interacționa cu aplicația. Aici se pot pune sortările, ștergerile din listă sau orice alte operațiuni necesare. În cazul de față doar 2 comenzi, GetProduct și SaveProduct ce vor fi folosite pentru a aduce un obiect din model în viewmodel și pentru a salva un produs. Acestea sunt de tipul ICommand și au o sintaxă de tipul :
public ICommand SaveProductCommand{
get{
if (_saveProductCommand == null) {
_saveProductCommand = new RelayCommand(param => SaveProduct(),
param => (CurrentProduct != null)); }
return _saveProductCommand;
}
}
Tot aici apare și clasa RelayCommand, esențială pentru ca MVVM să funcționeze. Aceasta conține o comandă ce va fi executată de alte clase pentru a rula cod în clasa de bază prin invocarea de delegates.
View-ul este partea care definește modul în care va arăta aplicația. De asemenea se vor defini două DataTemplate-uri, unul pentru model și unul pentru viewmodel.
Pentru a porni aplicația mai trebuie adăugate următoarele linii de cod în App.xaml.cs, care crează ViewModel-ul și asociază datacontext-ul ferestrei în viewmodel:
MainWindow app = new MainWindow();
ProductViewModel viewModel = new ProductViewModel();
app.DataContext = viewModel;
app.Show();
Și totuși când ar trebui să folosim acest model? Deși structura lui este una logică și oferă organizare în cod, acesta nu se pretează pentru orice fel de proiect. Dacă avem de-a face cu un proiect unde nu există o interfață grafică prea complexă nu se prea justifică folosirea MVVM, codul putând fi scris chiar și în code behind. De asemenea unii programatori nu recomandă folosirea acestui pattern dacă nu se dorește realizarea de unit-teste. Întrucât ușurința în scrierea testelor este unul din avantajele acestuia, este de preferat ca acesta să fie folosit doar împreună cu acestea. Este total neproductivă și deci nu este recomandată folosirea unui design pattern într-o aplicație simplă "Hello World!". Orice dezvoltator software poate să înțeleagă câteva linii de cod , chiar și dacă sunt scrise dezorganizat. Totuși, pe măsură ce numărul de linii de cod dintr-un program crește, crește și numărul funcționalităților.
Pe lângă dezavantajele amintite mai sus, ar mai fi și faptul că în unele cazuri poate fi dificilă proiectarea viewmodel-ului în așa fel încât să se obțină gradul de generalitate dorit și de asemenea debug-ul pentru data binding este mai greu de realizat față de metodele clasice care foloseau code behind.
În concluzie MVVM oferă anumite avantaje precum separarea view-ului de logica business și ușurința în scrierea testelor unitare, însă programatorii trebuie să fie atenți dacă acest design pattern se pretează sau nu pentru proiectul lor.