Rispondendo sui newsgroup mi sono reso conto, o forse è solo ego ;-), che probabilmente mancano risorse introduttive, molto introduttive, su Model-View-ViewModel. Non ho cercato, semplicemente mi piace scrivere, quindi sorbitemi :-), se proprio cambiate canale, aka “mark as read” ;-)
Disclaimer:
Un pattern in quanto tale è una linea guida per la soluzione ad un problema comune e in quanto linea guida va calato nel contesto del problema che deve risolvere, non va aplicato alla cieca in maniera “talebana”. Nel mio approccio all’uso di un pattern ho però scoperto che se il “primo e iniziale” approccio è un po’ talebano alla lunga paga perchè ogni volta che si devia dal pattern ci si sofferma a chiedersi perchè e quando non si ha esperienza di un dominio chiedersi perchè è “tanta roba” :-)
What’s that…?
mi sono già belle che rotto di scrivere M-V-VM quindi diventa il “nostro amico”… :-), il nostro amico è un pattern architetturale il cui focus è la presentazione dei dati, esiste da che mondo è mondo e non nasce con Wpf, è sempre stato conosciuto come Presentation Pattern; ma perchè è diventato un must, e per certi versi una buzz-word, proprio con Wpf? perchè il nostro amico trae enormi vantaggi dall’infinita potenza, flessibilità e malleabilità del motore di Data Binding proprio di Wpf.
Tornando in topic… un pattern per la presentazione dei dati è tipicamente composto da una triade, tre attori ognuno con un ruolo ben preciso:
  • Visualizzare i dati: è il ruolo specificamente dedicato alla visualizzazione dei dati, questo ruolo non ha nessun diritto di fare ragionamenti sui dati, può nella migliore delle ipotesi fare ragionamenti che determinano alcune caratteristiche della visualizzazione sulla base di caratteristiche dei dati stessi: coloro di rosso il nome di un prodotto perchè non è in stock;
  • Manipolare e preparare i dati: i dati tipicamente non sono pronti per essere visualizzati, la visualizzazione è spesso un appiattimento dei dati, avevte un grafo, in memoria, che è multi-dimensionale e volete rappresentarlo su un qualcosa che multi-dimensionale non è, esempio estremo un report cartaceo; questo ruolo ha quindi l’onere di trasformare e adattare i dati durante il loro ciclo di vita/caso d’uso;
  • Dati duri e puri: i nostri dati veri e proprio, il dominio, nulla di più ne nulla di meno senza nessunissima nozione di essere trattato e visualizzato in un’applicazione di qualsivoglia genere;
questi attori nell’idioma del nostro amico sono:
  • View: visualizza i dati;
  • ViewModel: manipola e prepara i dati;
  • Model: i dati quelli del nostro object model;
image
Un aspetto molto importante, di cui ho già avuto modo di discutere esponendo uno scenario si complesso ma decisamente tipico, è che il modello non deve mai essere esposto alla view ma deve sempre essere mediato dal view model, in realtà con l’esperienza scopriete che non è sempre necessariamente così ma è bene, soprattutto all’inizio, tenere ben presente questa regola.
What can M-V-VM do for me?
La domanda vera però probabilmente è un’altra: perchè?, perchè dovrei dannarmi per adottare il “nostro amico”? quali vantaggi ne possono trarre? alla fine dobbiamo capire se lo sbattimento il gioco vale la candela.
Separation of Concern
Da punto di vista del design sicuramente l’applicazione di un pattern di presentazione aiuta nel rispetare il principio di seprazione dei compiti, non è onere della UI prendere decisioni di business come non è onere del modello prendere decisioni sul suo shaping perchè sa di essere ospitao in una UI di un certo tipo; è inoltre indubbio che SoC aumenta la semplicità di manutenzione al crescere delle dimensioni della soluzione perchè sapere, ad esempio, dove cercare è un grosso vantaggio;
Tastability
Altro vantaggio portato da un pattern di questo tipo è che introduce la testabilità di buona parte del codice delegato alla manipolazione dei dati e l’unica cosa che ci rimane da verificare è chi abbia “composto” la UI abbia correttamente messo in binding le cose giuste con i dati giusti, ma per questo ci sono gli User Acceptance Test;
Separation of Roles
…l’unica cosa che ci rimane da verificare è chi abbia “composto” la UI abbia correttamente…
Finalmente, anche se ancora con un po’ di fatica, ma siamo sulla strada giusta, chi “disegna” non è detto che debba essere chi “programma”: M-V-VM, la tecnologia e gli strumenti finalmente ci permettono di separare abbastanza nettamente le figure professionali coinvolte nella creazione di un’applicazione Wpf.
Ma… ci sono degli svantaggi? naturalmente si:
  • L’applicazion di un presentation pattern è complessivamente un’operazione complessa, che richiede dimistichezza con il pattern e con la tecnologia che si vuole usare;
  • Alla complessità intrinseca dobbiamo aggiungere, ad oggi, che il rapporto tra wpf e il nostro amico è ancora agli inzi e molte aree sono borderline e diventa diffcile scegliere cosa far fare a chi, in questo senso essere un po’ talebani nell’applicazione del pattern aiuta a soffermarsi a pensare;
  • Come qualsiasi pattern è senza strumenti di controllo, non abbiamo un compilatore che ci dice che una certa cosa è sbagliata ergo è molto facile farsi prendere dalla fretta del fare le cose… e la fretta produce solo cose deprecabili;
My First M-V-VM Based application
Ok… tante belle parole ma in soldoni?
Come utente voglio poter visualizzare il dettaglio dell’anagrafica di una persona visualizzando nome, cognome, data di nascita ed età al fine di avere informazioni che non ho.
Abbiamo il requisito, che potrebbe produrre un modello di questo genere:
image
è evidente, ed è pure voluto in questo caso anche se magari è un po’ tirato per i capelli, che sul modello manca un’informazione essenziale: l’età. Quale miglior candidato del ViewModel per gestire questa informazione “calcolata”?
image
Come possiamo notare un ViewModel è una classe come tutte le altre nulla di più ne nulla di meno, l’unica caratteristica peculiare è che implementa l’interfaccia INotifyPropertyChanged, all’inizio un errore che si tende a fare è far derivare i propri view model da Dependency Object ma questo non porta nessun vantaggio, anzi porta (imho) qualche svantaggio.
ndr:
abbiamo un modello e uno strato di business (perchè alla fine il ViewModel questo è) implementato e funzionante, cosa ci vieta di testarlo? cosa ci avrebbe vietato di realizzarlo con un approccio basato su TDD o su Test First? Inoltre è interessante sottolineare che fino ad ora non abbiamo minimamente tirato in ballo Wpf.

Dopo la divagazione, che mette in luce un punto interessante e importante, cosa ci resta da fare? Unire i 2 mondi, cominciamo con il disegnare la UI:
image
decisamente banale, ma per ora fa il suo sporco lavoro :-)
Cosa ci resta da fare? molto banalemente collegare i 2 mondi. Discuteremo del concetto di view-first e view-model-first, per ora limitiamoci a farlo funzionare con un' approccio view-first senza entrare nel dettaglio:
<Window x:Class="Mvvm.Presentation.MainView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:presentation="clr-namespace:Mvvm.Presentation"
        Title="MainView" Height="158" Width="300">
    <Window.DataContext>
        <presentation:MainViewModel />
    <Window.DataContext>
Definiamo il nostro namespace xml “presentation” che ci consente di avere accesso dallo xaml direttamente ai nostri tipi e poi assegnamo alla proprietà DataContext della Window un’istanza del nostro ViewModel.
A runtime quello che succede è questo:

image
La nostra Window viene istanziata e i dati visualizzati, mettendo in luce la mia tenera età :-), ma quale magia fa si che i dati giusti compaiano al posto giusto?
<TextBox Grid.Column="1" Height="23" 
            Text="{Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged}"
            HorizontalAlignment="Stretch" VerticalAlignment="Top" />
In realtà nessuna magia piuttosto una “banale” espressione di binding con cui indichiamo al motore di Data Binding di Wpf come vogliamo che 2 elementi vengano “interconnessi” tra loro, figo :-)
Direi che come panoramica iniziale abbiamo messo abbastanza carne al fuoco, naturalmente ci sono anche i sorgenti.

Per ogni cosa fate un fischio :-)
.m