MVVM e modello di servizio

10

Sto costruendo un'applicazione WPF usando il pattern MVVM. In questo momento, i miei viewmodels chiamano il livello di servizio per recuperare i modelli (come non è rilevante per il viewmodel) e convertirli in viewmodels. Sto utilizzando l'iniezione del costruttore per passare il servizio richiesto a viewmodel.

È facilmente testabile e funziona bene per i modelli con poche dipendenze, ma appena provo a creare viewModels per i modelli complessi, ho un costruttore con un sacco di servizi iniettati (uno per recuperare ogni dipendenza e un elenco di ad esempio tutti i valori disponibili per associare a una fonte di elementi). Mi sto chiedendo come gestire più servizi come quello e ho ancora un viewmodel che posso testare facilmente.

Sto pensando ad alcune soluzioni:

  1. Creazione di servizi singleton (IServices) contenenti tutti i servizi disponibili come interfacce. Esempio: Services.Current.XXXService.Retrieve (), Services.Current.YYYService.Retrieve (). In questo modo, non ho un enorme costruttore con una tonnellata di parametri di servizio in essi.

  2. Creazione di una facciata per i servizi utilizzati da viewModel e passaggio di questo oggetto nel ctor del mio viewmodel. Ma poi, dovrò creare una facciata per ciascuno dei miei modelli di visi complessi, e potrebbe essere un po 'troppo ...

Quale pensi sia il modo "giusto" per implementare questo tipo di architettura?

    
posta alfa-alfa 04.11.2013 - 16:21
fonte

3 risposte

17

In effetti, entrambe queste soluzioni sono pessime.

Creating a services singleton (IServices) containing all the available services as interfaces. Example: Services.Current.XXXService.Retrieve(), Services.Current.YYYService.Retrieve(). That way, I don't have a huge constructor with a ton of services parameters in them.

Questo è essenzialmente il Pattern Locator del servizio , che è un anti-pattern. Se lo fai, non sarai più in grado di capire di cosa sia effettivamente il modello di visualizzazione, senza considerare la sua implementazione privata, il che renderà molto difficile il test o il refactoring.

Creating a facade for the services used by the viewModel and passing this object in the ctor of my viewmodel. But then, I'll have to create a facade for each of my complexe viewmodels, and it might be a bit much...

Questo non è tanto un anti-pattern ma è un odore di codice. Essenzialmente stai creando un oggetto parametro , ma il punto del pattern di refactoring PO è quello di gestire i set di parametri che sono usati frequentemente e in molti posti diversi , mentre questo parametro dovrebbe essere usato solo una volta. Come hai detto, creerebbe un sacco di codice gonfiato senza alcun beneficio reale e non sarebbe stato piacevole con molti contenitori IoC.

In effetti, entrambe le strategie di cui sopra stanno trascurando il problema generale, ovvero che l'accoppiamento è troppo alto tra i modelli di visualizzazione e i servizi . Semplicemente nascondere queste dipendenze in un localizzatore di servizi o in un oggetto parametro in realtà non modifica di quanti altri oggetti dipende dal modello di visualizzazione.

Pensa a come testare unitamente uno di questi modelli di visualizzazione. Quanto sarà grande il tuo codice di installazione? Quante cose devono essere inizializzate affinché funzioni?

Molte persone che iniziano con MVVM cercano di creare modelli di visualizzazione per uno intero schermo , che è fondamentalmente l'approccio sbagliato. MVVM riguarda la composizione e una schermata con molte funzioni dovrebbe essere composta da diversi modelli di visualizzazione, ognuno dei quali dipende da uno o pochi modelli / servizi interni. Se hanno bisogno di comunicare tra loro, lo fai tramite pub / sub (broker di messaggi, bus eventi, ecc.)

Ciò che in realtà è necessario è il refactoring dei tuoi modelli di vista in modo che abbiano meno dipendenze . Quindi, se è necessario disporre di uno "schermo" aggregato, si crea un altro modello di vista per aggregare i modelli di vista più piccoli. Questo modello di vista aggregato non deve fare molto da solo, quindi a sua volta è anche abbastanza facile da capire e testare.

Se l'hai fatto correttamente, dovrebbe essere ovvio solo guardando il codice, perché avrai modelli di visualizzazione brevi, concisi, specifici e testabili.

    
risposta data 04.11.2013 - 19:35
fonte
1

Potrei scrivere un libro su questo ... infatti io sono;)

Prima di tutto, non esiste un modo "giusto" universalmente per fare le cose. Devi prendere in considerazione altri fattori.

È possibile che i tuoi servizi siano troppo granulosi. Racchiudere i servizi con facciate che forniscono l'interfaccia che un Viewmodel specifico o anche un cluster di ViewModels correlati utilizzerebbe potrebbe essere una soluzione migliore.

Ancora più semplice sarebbe avvolgere i servizi in un'unica facciata che tutti i viewmodels utilizzano. Ovviamente può essere potenzialmente un'interfaccia molto grande con molte funzioni non necessarie per lo scenario medio. Ma direi che non è diverso da un router di messaggi che gestisce ogni messaggio nel tuo sistema.

In effetti, ciò a cui ho visto evolversi molte architetture è un message bus costruito attorno a qualcosa come il pattern Event Aggregator. Il test è facile perché la tua classe di test registra solo un ascoltatore con l'EA e fa scattare l'evento appropriato in risposta. Ma questo è uno scenario avanzato che richiede tempo per crescere. Dico iniziare con la facciata unificante e andare da lì.

    
risposta data 04.11.2013 - 20:43
fonte
0

Perché non combinare entrambi?

Crea una facciata e metti tutti i servizi che utilizzano i tuoi modellini di vista. Quindi puoi avere una facciata singola per tutti i tuoi modelli di vista senza la parola S sbagliata.

Oppure puoi usare l'iniezione di proprietà invece dell'iniezione del costruttore. Ma poi, devi assicurarti che vengano iniettati correttamente.

    
risposta data 04.11.2013 - 16:24
fonte

Leggi altre domande sui tag