2010-03-25 12 views
14

Sto scrivendo un'applicazione PRISM/MVVM/WPF. È un'applicazione LOB, quindi ci sono un sacco di regole complicate. Ho notato che il View Model sta iniziando a gonfiarsi. Ci sono due problemi principali.Refactoring Bloated ViewModel

Uno è che per mantenere MVVM, sto facendo un sacco di cose che ritengono hacky come aggiungere un sacco di proprietà alla mia VM. La vista si lega a quelle proprietà per tenere traccia di ciò che sembra ricevere informazioni specifiche. Ad esempio, un booleano tiene traccia dello stato di un lungo processo in esecuzione nella VM, quindi la vista può disabilitare alcuni dei suoi controlli mentre il processo a lunga esecuzione funziona. Ho letto che questo problema potrebbe essere risolto con Attached Behaviors. Guarderò di più in questo. Nelle app MVVM di esempio che vedi online, questo non è un grosso problema perché sono troppo semplificate.

L'altro problema è il numero di comandi nella mia VM. In questo momento ci sono quattro comandi. Sto definendo i comandi nella VM usando RelayCommand di Josh Smith (fondamentalmente il DelegateCommand in PRISM) in modo che tutta la logica aziendale viva nella VM. Considerai di spostare ciascun comando in un'unità di lavoro separata. Non sono sicuro che il modo migliore per farlo.

Quali modelli state usando per mantenere pulite le vostre macchine virtuali? Posso già sentire qualcuno rispondere con "il tuo punto di vista e la VM è troppo complicata, dovresti suddividerli in molte view/VM". Non è certamente troppo complicato da una prospettiva di Ux: ci sono 2 pulsanti, una combobox e una listbox. Inoltre, da una prospettiva logica, è un dominio coesivo. Detto questo, sono molto interessato ad ascoltare come gli altri hanno a che fare con questo tipo di problema.

Grazie per l'input.

+1

commento stupido, ma non posso resistere: "cosa c'è di sbagliato con gonfiare"? È un modo di vivere, un modo di sviluppo del software ;-) –

+0

Ottima domanda. Il mio viewPodel WPF è pieno di hack per aggirare le limitazioni in WPF quando si cerca di fare correttamente MVVM. La maggior parte di queste cose è banale in un'app MVP WinForms. – Damien

+0

Potrebbe essere possibile verificare MVPVM. Separa essenzialmente la VM in modo che la VM si occupi solo di associazione dati e la Presenter gestisce i comandi. Non mi piace il modo in cui perdi la comodità del comando bind ma lo fa per una VM più pulita. – bygrace

risposta

4

Sento il tuo dolore. Combatto molto con questo tipo di domande quando lavoro su app MVVM. Uno di questi giorni pubblicherò una lista di domande per ottenere input da altri come te.

Tendo a preoccuparmi molto di "ingigantire" nella mia classe base ViewModel ma non tanto nelle sottoclassi di ViewModel concrete. È spesso allettante lanciare una dipendenza usata da 2-3 ViewModels nella classe base, ma dovrebbe essere evitata.

Mentre non posso presumere di sapere qual è la tua idea di gonfiamento, posso dire che non penso che avere la proprietà "occupata" oi comandi gestiti nella VM siano cattivi. Una cosa che potresti considerare è se il ViewModel può essere occupato o meno a fare più di una cosa alla volta. Se è così, potresti voler andare avanti e pensare a come scomporlo. Anche se non l'ho visto personalmente nella pratica, probabilmente potresti avere la tua singola visione coesa e un paio di ViewModels legati ad essa.

Se i comandi sono lunghi o se possono essere eseguiti su target diversi, penso che rendere i comandi unità autoeseguite sia una buona idea. Ma probabilmente è meglio essere coerenti con questo approccio per evitare di confondere chiunque lavori su di esso in seguito. Ad esempio, se si dispone di una classe SaveCustomerCommand di circa 10 righe di codice, probabilmente non si desidera utilizzare un RelayCommand per tutto il resto.

Sarebbe bello se esistessero regole rigide e veloci per questo genere di cose, ma penso che sia la struttura che il modello siano ancora nella fase evolutiva in questo momento.

+0

Grazie per la tua risposta Josh. Sì, sono d'accordo sul fatto che il quadro sia ancora giovane. Bloated = più di un modo di cambiare, violando così SRP. – Noel

+0

Oh, beh allora in quel caso ... buona fortuna! :) Penso che SRP sia totalmente sopravvalutato. Nel mondo reale prescrive fondamentalmente un metodo per classe. Anche se questo può andar bene per le classi di comando, non sono solo venduto per applicarlo a modelli e visori. – Josh

0

Senza conoscere le specifiche del ViewModel è difficile dire da dove proviene il rigonfiamento. Potrebbe essere non correlato alla UI, come troppe linee per interrogare il modello. Oppure potrebbero essere caratteristiche dell'interfaccia utente che sono meglio implementate tutte nell'interfaccia utente con trigger o qualcosa del genere.

Potresti elaborare il tuo ViewModel?

MODIFICA In base ai commenti.

SaveCustomer e DeleteCustomer suonano come i metodi Model o Service, quindi li inserisco in una sorta di Persistence Layer.

Carica/scarica video cliente: ancora, questi non sono specifici di ViewModel (è possibile che si desideri eseguire questi in un altro ViewModel), quindi li inserirò in un servizio Web e il comando ViewModel lo chiamerà.

In generale vale la pena inserire un codice comune (che più ViewModel potrebbe desiderare di utilizzare) in un servizio e quindi lanciare quel servizio nel proprio IOC. In questo modo i tuoi ViewModel finiscono per essere "registi" leggeri di informazioni dalla Vista al Modello o al Servizio.

+0

Il bloat proviene dai 4 comandi e dalle proprietà specifiche della vista come descritto sopra.La mia domanda è astratta: quali sono i modelli che le persone stanno usando per astrarre la logica in unità di lavoro al di fuori del ViewModel? Dove traccia la linea tra la vista e la logica specifica della VM? Come esempio, utilizziamo questi quattro comandi: Salva cliente, Elimina cliente, Carica video cliente (di lunga durata), Scarica video cliente (di lunga durata). La vista deve essere consapevole dello stato dei comandi video di lunga durata. – Noel

+0

e grazie per la risposta :) – Noel

+0

I RelayCommands chiamano componenti e servizi diversi in altri livelli dell'applicazione, ma ho ancora bisogno dei RelayCommands definiti nella mia VM in modo che la mia vista possa collegarli. Quindi per il comando di download video del cliente, la VM chiama un webservice per ottenere il video, esegue una manipolazione a livello di byte sulla risposta, quindi apre il video utilizzando un componente diverso. Forse potrei estrarre la carne della logica RelayCommand/DelegateCommand in una classe diversa e rendere RelayCommands una faccetta sottile che si limita a delegare alla nuova classe. – Noel

0

Ho due pensieri principali su questo, ma YMMV.

In primo luogo sposterei il codice specifico di implementazione dalla VM in "servizio" che è fondamentalmente solo una classe che esegue il lavoro. Può trattarsi ad esempio di una classe "customerService" che ha l'implementazione dei metodi save, delete, update e l'implementazione RelayCommand chiamerà il servizio per eseguire l'azione e aggiornerà le proprietà di visualizzazione come appropriato.

Un esempio potrebbe essere che RelayCommand può impostare il valore di una proprietà "ShowProgressBar", che è associata alla visibilità di una barra di avanzamento. Avrebbe quindi chiamato la funzione di salvataggio nel servizio e, una volta completato (dovrebbe essere asincrono btw), dovrebbe aggiornare di nuovo "ShowProgressBar". In questo modo ViewModel controlla lo stato della vista indipendentemente dalla logica dell'applicazione principale.

In secondo luogo, esaminerei il modello di visualizzazione di base che si sta utilizzando e cerco di mantenerlo il più semplice possibile, aggiungendo solo componenti comuni a tutti i modelli di visualizzazione. È quindi possibile creare altri modelli di vista di base o interfacce in base alle esigenze. Ad esempio, potresti decidere che tutti i tuoi modelli di visualizzazione esporranno una proprietà ShowProgressBar che può essere associata a una barra di avanzamento sulla tua pagina, oppure potresti decidere di creare un ProgressViewModelBase o IAllowProgress che erediti/implementa in pagine che richiedono barre di avanzamento.

La vista stessa dovrebbe avere il minor numero possibile di codice, si dovrebbe mirare a 0 code-behind (anche se ci sono cose che possono essere fatte solo in code-behind sfortunatamente). Una cosa che ho trovato a mio discapito è che la creazione di istanze di viewmodels complessi può essere costosa in termini di prestazioni, soprattutto se si registrano relaycommands e sorgenti/ascoltatori di eventi aggregatori, quindi è molto utile mantenere il modello viewmodel slim. Ciò è particolarmente importante se si utilizza ViewModelCollections per visualizzare gli elenchi degli oggetti Model e dove si verifica la creazione ViewModel sul thread dell'interfaccia utente (che è probabile se il ViewModel viene istanziato nella Creazione vista).