MVVM è inutile? [chiuso]


91

L'implementazione MVVM ortodossa è inutile? Sto creando una nuova applicazione e ho considerato Windows Form e WPF. Ho scelto WPF perché è a prova di futuro e offre molta flessibilità. C'è meno codice ed è più facile apportare modifiche significative alla tua interfaccia utente usando XAML.

Poiché la scelta per WPF è ovvia, ho pensato che potevo anche andare fino in fondo utilizzando MVVM come architettura dell'applicazione poiché offre miscelabilità, problemi di separazione e testabilità dell'unità. In teoria, sembra bello come il Santo Graal della programmazione dell'interfaccia utente. Questa breve avventura; tuttavia, si è trasformato in un vero e proprio mal di testa. Come previsto in pratica, sto scoprendo di aver scambiato un problema con un altro. Tendo ad essere un programmatore ossessivo in quanto voglio fare le cose nel modo giusto in modo da poter ottenere i giusti risultati e possibilmente diventare un programmatore migliore. Il modello MVVM ha appena bocciato il mio test sulla produttività e si è appena trasformato in un grosso trucco schifoso!

Il chiaro esempio calzante è l'aggiunta del supporto per una finestra di dialogo modale. Il modo corretto è creare una finestra di dialogo e collegarla a un modello di visualizzazione. Far funzionare tutto questo è difficile. Per trarre vantaggio dal pattern MVVM, è necessario distribuire il codice in più punti attraverso i livelli dell'applicazione. Devi anche usare costrutti di programmazione esoterici come modelli ed espressioni lamba. Cose che ti fanno guardare lo schermo grattandoti la testa. Ciò rende la manutenzione e il debug un incubo in attesa di accadere come ho scoperto di recente. Avevo una casella di informazioni che funzionava bene finché non ho ricevuto un'eccezione la seconda volta che l'ho invocata, dicendo che non poteva mostrare di nuovo la finestra di dialogo una volta chiusa. Ho dovuto aggiungere un gestore di eventi per la funzionalità di chiusura alla finestra di dialogo, un altro nell'implementazione di IDialogView e infine un altro in IDialogViewModel. Pensavo che MVVM ci avrebbe salvato da tali stravaganti hacker!

Ci sono molte persone là fuori con soluzioni concorrenti a questo problema e sono tutti hack e non forniscono una soluzione pulita, facilmente riutilizzabile ed elegante. La maggior parte dei toolkit MVVM sorvola sulle finestre di dialogo e quando le affrontano, sono solo caselle di avviso che non richiedono interfacce personalizzate o modelli di visualizzazione.

Ho intenzione di rinunciare al modello di visualizzazione MVVM, almeno alla sua implementazione ortodossa. Cosa ne pensi? È valsa la pena per te se ne avessi avuto? Sono solo un programmatore incompetente o MVVM non è quello che si propone di essere?


6
Mi sono sempre chiesto se MVVM sia o meno over-engineering. Domanda interessante.
Taylor Leese

11
Pattern come MVVM e MVC sembrano eccessivi, finché non devi eseguire alcune modifiche o cambiare un componente. La prima volta che devi farlo, tutta la cerimonia si ripaga da sola.
Robert Harvey,

41
Le lambda sono esoteriche? novità per me.
Ray Booysen

5
@ Ray - Haha, +1 per quel commento! : D
Venemo

7
Come ha sottolineato Alan Cooper oltre un decennio fa in About Face , se stai progettando interfacce utente e dialoghi modali non sono un caso limite, probabilmente stai facendo qualcosa di sbagliato.
Robert Rossney,

Risposte:


61

Scusa se la mia risposta è diventata un po 'lunga, ma non prendertela con me! Anche la tua domanda è lunga.

In sintesi, MVVM non è inutile.

Il chiaro esempio calzante è l'aggiunta del supporto per una finestra di dialogo modale. Il modo corretto è creare una finestra di dialogo e collegarla a un modello di visualizzazione. Far funzionare tutto questo è difficile.

Sì, lo è davvero.
Tuttavia, MVVM fornisce un modo per separare l'aspetto dell'interfaccia utente dalle sue logiche. Nessuno ti obbliga a usarlo ovunque e nessuno ti tiene una pistola contro la fronte per farti creare un ViewModel separato per tutto.

Ecco la mia soluzione per questo particolare esempio: il
modo in cui l'interfaccia utente gestisce un determinato input non è affare di ViewModel. Aggiungerei codice al file .xaml.cs della vista, che crea un'istanza della finestra di dialogo e imposta la stessa istanza ViewModel (o qualcos'altro, se necessario) come DataContext.

Per trarre vantaggio dal pattern MVVM, è necessario distribuire il codice in più punti attraverso i livelli dell'applicazione. Devi anche usare costrutti di programmazione esoterici come modelli ed espressioni lamba.

Bene, non devi usarlo in diversi posti. Ecco come lo risolverei:

  • Aggiungi XAML alla visualizzazione e niente in .xaml.cs
  • Scrivi ogni logica dell'app (eccetto le cose che funzionerebbero direttamente con gli elementi dell'interfaccia utente) all'interno di un ViewModel
  • Tutto il codice che dovrebbe essere eseguito dall'interfaccia utente ma che non ha nulla a che fare con la logica aziendale va nei file .xaml.cs

Penso che lo scopo di MVVM sia principalmente quello di separare la logica dell'applicazione e l'interfaccia utente concreta, consentendo quindi facili modifiche (o sostituzione completa) dell'interfaccia utente.
Uso il seguente principio: la vista può conoscere e assumere tutto ciò che vuole dal ViewModel, ma il ViewModel non può sapere NIENTE della vista.
WPF fornisce un bel modello di associazione che puoi usare per ottenere esattamente questo.

(A proposito, i modelli e le espressioni lambda non sono esoterici se usati correttamente. Ma se non vuoi, non usarli.)

Cose che ti fanno guardare lo schermo grattandoti la testa.

Sì, conosco la sensazione. Esattamente quello che provavo quando ho visto MVVM per la prima volta. Ma una volta capito, non ti sentirai più male.

Avevo una scatola che funzionava bene ...

Perché dovresti mettere un ViewModel dietro una scatola? Non ha senso.

La maggior parte dei toolkit MVVM sorvola sulle finestre di dialogo e quando le affrontano, sono solo caselle di avviso che non richiedono interfacce personalizzate o modelli di visualizzazione.

Sì, perché il fatto stesso che un elemento dell'interfaccia utente si trovi nella stessa finestra, o in un'altra finestra, o stia orbitando attorno a Marte al momento non è una preoccupazione dei ViewModels.
Separazione degli interessi

MODIFICARE:

Ecco un video molto carino il cui titolo è Build your own MVVM framework . Vale la pena guardare.


2
+1 per le ultime tre parole. Ma anche il resto della risposta è buono. :)
Robert Harvey,

12
+1 per i consigli sull'utilizzo del code-behind. È un malinteso comune che sia "cattivo" usare il code-behind in MVVM ... ma per cose puramente relative all'interfaccia utente, questa è la strada da percorrere.
Thomas Levesque

@ Thomas: Sì, non potrei essere più d'accordo. Ho visto diverse implementazioni in cui le persone inseriscono tutto il codice (anche relativo all'interfaccia utente) nel ViewModel, perché (secondo loro) "è lì che si trova il codice". Era piuttosto hacky.
Venemo

3
@Venemo, penso che tu possa incapsulare molte delle cose che vorresti inserire nel code-behind usando tecniche come comportamenti personalizzati, che è utile se ti ritrovi a scrivere ripetutamente codice colla. In generale, però, penso che sia meglio usare il code-behind per la colla che per hackerare insieme un XAML imbarazzante. La preoccupazione principale, nella mia mente, è assicurarsi che non ci sia nulla nel code-behind abbastanza sofisticato da giustificare i test unitari. Qualsiasi cosa sufficientemente complessa è meglio incapsulata nel ViewModel o in una classe di estensione, come Behavior o MarkupExtension.
Dan Bryant

7
@ Thomas: hai ragione, il più grande mito su MVVM è che lo scopo di MVVM è sbarazzarsi del code-behind. Lo scopo è quello di eliminare il codice non-UI dal codice sottostante. Inserire il codice della sola interfaccia utente nel ViewModel è altrettanto dannoso quanto inserire il codice di dominio problematico nel code-behind.
Jim Reineri

8

Far funzionare tutto questo è difficile. Per trarre vantaggio dal pattern MVVM, è necessario distribuire il codice in diversi punti in tutti i livelli dell'applicazione. Devi anche usare costrutti di programmazione esoterici come modelli ed espressioni lamba.

Per una normale finestra di dialogo modale? Stai sicuramente facendo qualcosa di sbagliato lì: l'implementazione di MVVM non deve essere così complessa.

Considerando che sei nuovo sia a MVVM che a WPF, è probabile che tu stia utilizzando soluzioni non ottimali ovunque e complichi inutilmente le cose - almeno l'ho fatto quando sono passato a WPF. Assicurati che il problema sia davvero MVVM e non la tua implementazione prima di arrenderti.

MVVM, MVC, Document-View, ecc. È una vecchia famiglia di pattern .. Ci sono degli svantaggi, ma nessun difetto fatale del tipo che descrivi.


5

Sono nel mezzo di uno sviluppo MVVM piuttosto complesso utilizzando PRISM, quindi ho già dovuto far fronte a questo tipo di preoccupazioni.

Le mie conclusioni personali:

MVVM contro MVC / PopUps & co

  • MVVM è davvero un ottimo modello e nella maggior parte dei casi sostituisce completamente MVC grazie al potente data binding in WPF
  • Chiamare il livello di servizio direttamente dal presentatore è un'implementazione legittima nella maggior parte dei casi
  • Anche scenari List / Detail piuttosto complessi possono essere implementati da MVVM puro grazie alla sintassi {Binding Path = /}
  • Tuttavia, quando è necessario implementare un coordinamento complesso tra più visualizzazioni, un controller è obbligatorio
  • Possono essere utilizzati eventi; il vecchio modello che implica la memorizzazione di istanze di IView (o AbstractObserver) nel controller è obsoleto
  • Il controller può essere iniettato in ogni contenitore Presenter by IOC
  • Il servizio IEventAggregator di Prism è un'altra possibile soluzione se l'unico utilizzo del controller è l'invio di eventi (in questo caso può sostituire completamente il controller)
  • Se le viste devono essere create dinamicamente, questo è un lavoro molto adatto per il controller (in prism il controller verrà iniettato (IOC) un IRegionManager)
  • Le finestre di dialogo modali sono per lo più obsolete nelle moderne applicazioni composite, ad eccezione delle operazioni di blocco reali come le conferme obbligatorie; in questi casi l'attivazione modale può essere astratta come un servizio chiamato all'interno del controller, e implementato da una classe specializzata, che consente anche di test di unità a livello di presentazione avanzato. Il controller, ad esempio, chiamerà IConfirmationService.RequestConfirmation ("sei sicuro") che attiverà una finestra di dialogo modale in fase di esecuzione e può essere facilmente deriso durante il test di unità

5

Mi occupo della questione dei dialoghi barando. My MainWindow implementa un'interfaccia IWindowServices che espone tutte le finestre di dialogo specifiche dell'applicazione. Gli altri miei ViewModel possono quindi importare l'interfaccia dei servizi (io uso MEF, ma potresti semplicemente passare l'interfaccia manualmente attraverso i costruttori) e usarla per realizzare ciò che è necessario. Ad esempio, ecco come appare l'interfaccia per una mia piccola applicazione di utilità:

//Wrapper interface for dialog functionality to allow for mocking during tests
public interface IWindowServices
{
    bool ExecuteNewProject(NewProjectViewModel model);

    bool ExecuteImportSymbols(ImportSymbolsViewModel model);

    bool ExecuteOpenDialog(OpenFileDialog dialog);

    bool ExecuteSaveDialog(SaveFileDialog dialog);

    bool ExecuteWarningConfirmation(string text, string caption);

    void ExitApplication();
}

Questo mette tutte le esecuzioni di Dialog in un unico posto e può essere facilmente cancellato per i test di unità. Seguo lo schema che il client della finestra di dialogo ha per creare il ViewModel appropriato, che può quindi configurare secondo necessità. La chiamata Execute si blocca e successivamente il client può guardare il contenuto del ViewModel per vedere i risultati della finestra di dialogo.

Un design MVVM più "puro" può essere importante per un'applicazione di grandi dimensioni, dove è necessario un isolamento più pulito e una composizione più complessa, ma per le app di piccole e medie dimensioni, penso che un approccio pratico, con servizi appropriati per esporre i ganci richiesti, sia abbastanza sufficiente .


Ma dove lo chiami? Non sarebbe meglio costruire la finestra di dialogo all'interno delle funzioni della classe IWindowServices piuttosto che farla passare. In questo modo la vista del modello chiamante non dovrebbe sapere nulla della particolare implementazione della finestra di dialogo.
Joel Rodgers

L'interfaccia viene iniettata in qualunque delle mie istanze ViewModel necessiti di accesso alle finestre di dialogo dell'applicazione. Nella maggior parte dei casi, passo il ViewModel per la finestra di dialogo, ma sono diventato un po 'pigro e ho usato WPF OpenFileDialog e SaveFileDialog per le chiamate alla finestra di dialogo dei file. Il mio obiettivo principale era l'isolamento ai fini del test di unità, quindi questo è sufficiente per tale obiettivo. Se desideri un isolamento migliore, probabilmente vorrai creare OpenFileViewModel e SaveFileViewModel, che duplicherebbero le proprietà necessarie per le finestre di dialogo.
Dan Bryant

Nota che questo non è sicuramente un approccio puro, in quanto il ViewModel che utilizza le finestre di dialogo conosce il ViewModel specifico per ogni finestra di dialogo che desidera aprire. Penso che sia abbastanza pulito, ma potresti sempre aggiungere un ulteriore strato di isolamento con una classe che espone puramente i parametri richiesti per l'utilizzo del dialogo, nascondendo ogni visibilità non necessaria delle proprietà ViewModel utilizzate durante l'associazione. Per applicazioni più piccole, ritengo che questo isolamento aggiuntivo sia eccessivo.
Dan Bryant

5

I modelli di design sono lì per aiutarti, non per ostacolare. Una piccola parte dell'essere un buon sviluppatore è sapere quando "infrangere le regole". Se MVVM è ingombrante per un'attività e hai determinato che il valore futuro non vale lo sforzo, non utilizzare il modello. Ad esempio, come hanno commentato altri poster, perché dovresti esaminare tutte le spese generali per implementare una semplice scatola?

I modelli di progettazione non sono mai stati concepiti per essere seguiti dogmaticamente.


2
Corretta. Una semplice scatola dovrebbe essere semplice, ma cosa succede se devi visualizzare informazioni come versione, licenza, processo in esecuzione, nome dell'azienda, ecc. Queste sono tutte informazioni che risiedono da qualche parte. Nei moduli standard, puoi semplicemente associare tutte queste informazioni e farla finita. MVVM dice che dovresti creare un modello di visualizzazione per esso, che è ridondante.
ATL_DEV

1

Poiché il modello stesso, MVVM è fantastico. Ma la libreria di controllo di WPF fornita con il supporto per l'associazione dati NET 4.0 è molto limitata, è molto meglio di WinForm, ma non è ancora sufficiente per MVVM associabile, direi che la sua potenza è circa il 30% di ciò che è necessario per MVVM associabile.
Bindable MVVM: è l'interfaccia utente in cui ViewModel è cablato con View solo usando l'associazione dati.
Il pattern MVVM riguarda la rappresentazione dell'oggetto di ViewState, non descrive come si mantiene la sincronizzazione tra View e ViewModel, in WPF è il data binding ma può essere qualsiasi cosa. E in realtà puoi usare il pattern MVVM in qualsiasi toolkit dell'interfaccia utente che supporta eventi \ callback, puoi usarlo in WinAPI puro in WinForms (l'ho fatto, e non è molto più lavoro con eventi \ callbacks), e puoi persino usarlo in Text Console, come riscrivere Norton Commander di DoS utilizzando il pattern MVVM.

In breve: MVVM non è inutile, è fantastico. La libreria di controllo di NET 4.0 WPF è spazzatura.

Ecco la semplice dimostrazione del concetto ViewModel che non è possibile associare ai dati in puro modo MVVM utilizzando WPF.

public class PersonsViewModel
{
    public IList<Person> PersonList;
    public IList<ColumnDescription> TableColumns;
    public IList<Person> SelectedPersons;
    public Person ActivePerson;
    public ColumnDescription SortedColumn;
}

Non è possibile associare dati alle intestazioni di colonna DataGrid di WPF, non è possibile associare dati alle righe selezionate, ecc. Ecc. Puoi solo immaginare come le cose peggiorino con ViewModel complessi.
Quindi la risposta è semplice a meno che tu non stia scrivendo un'applicazione Hello World, l'uso di MVVM associabile in WPF è inutile. Trascorrerai la maggior parte del tuo tempo a pensare a un hack per vincolare ViewModel. Il data binding è carino ma sii pronto per il fallback al 70% del tempo dell'evento.


Puoi associarlo, con i convertitori, a un DataGrid.
Cameron MacFarland

@CameronMacFarland: Non tutte, alcune proprietà sono di sola lettura e non associabili, alcune semplicemente non esistono e ci sono solo eventi che cambiano lo stato del rapporto.
Alex Burtsev

Ammetto di non avere molta esperienza nell'uso di WPF DataGrid. Tendo ad evitarlo perché è brutto e non si adatta più a WPF. Detto questo, una combinazione di convertitori e AttachedProperties per gestire gli eventi dovrebbe darti ciò di cui hai bisogno.
Cameron MacFarland

1
Alex, i problemi che hai sono con il design di DataGrid, non con MVVM. È semplicemente sbagliato affermare che "l'associazione dei dati è piacevole ma sii pronto a eseguire il fallback nel 70% del tempo dell'evento". Ho scritto alcune applicazioni WPF oggettivamente enormi in cui non sono presenti gestori di eventi nell'interfaccia utente, ad eccezione del gestore di eventi di cui la griglia di dati (Telerik) necessita per l'inizializzazione.
Robert Rossney,

3
Penso che potresti avere più successo se invece di adottare l'atteggiamento "Questo è progettato male e non funziona", provassi, "Perché funziona per altre persone ma non per me?" Potresti scoprire che il motivo per cui le cose sono difficili da fare è che non sai ancora come farle.
Robert Rossney,

0

No, non è inutile, ma è difficile avvolgere la testa anche se lo schema stesso è ridicolmente semplice. Ci sono tonnellate di disinformazione là fuori e vari gruppi che combattono per il modo corretto. Penso che con WPF e Silverlight dovresti usare MVVM o finirai con la codifica e tenterai di risolvere i problemi in un nuovo modello la "vecchia" metodologia dei moduli vincenti che ti porta solo nei guai. Questo è più il caso di Silverlight poiché tutto è necessario per essere asincrono (gli hack attorno a questo sono possibili, ma dovresti semplicemente scegliere un'altra piattaforma).

Suggerirei di leggere questo articolo Semplificare il WPF TreeView utilizzando attentamente il pattern ViewModel per vedere come MVVM può essere implementato bene e consentire di cambiare la mentalità delle forme di vittoria nel nuovo modo di pensare in MVVM. In breve, quando vuoi fare qualcosa applica la logica al ViewModel prima e non alla View. Vuoi selezionare un articolo? Cambiare un'icona? Non iterare sugli elementi dell'interfaccia utente, aggiorna semplicemente le proprietà dei modelli e lascia che l'associazione dati faccia il nocciolo.


-1

Ho riscontrato lo stesso problema con molte implementazioni MVVM quando si tratta di dialoghi (modali). Quando guardo i partecipanti al pattern MVVM, ho la sensazione che manchi qualcosa per costruire un'applicazione coerente.

  • La vista contiene i controlli GUI specifici e definisce l'aspetto dell'interfaccia utente.
  • ViewModel rappresenta lo stato e il comportamento della presentazione.
  • Il modello può essere un oggetto di business dal livello di dominio o un servizio che fornisce i dati necessari.

Ma manca è:

  • Chi crea i ViewModels?
  • Chi è responsabile del flusso di lavoro dell'applicazione?
  • Chi fa da intermediario tra i ViewModel quando hanno bisogno di comunicare tra loro?

Il mio approccio è quello di introdurre un (Use-Case) controller che è responsabile per i punti mancanti. Come funziona può essere visto nelle applicazioni di esempio WPF Application Framework (WAF) .


Il modello Mediator implementato da Josh Smith ha risolto tutti i miei problemi di comunicazione View Model. Messenger.NotifyCollgue ha fornito un modo per avere modelli di visualizzazione completamente indipendenti che sapevano come rispondere agli eventi globali (se interessati) senza che due modelli di visualizzazione si conoscessero l'uno dell'altro. Ha già salvato la nostra pancetta un paio di volte.
JasonD
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.