2013-05-02 12 views
28

Dopo aver fatto un paio di progetti che utilizzano il modello MVVM, Im ancora alle prese con il ruolo del ViewModel:MVVM: ViewModel e Business Logic collegamento

Quello che ho fatto in passato: Usando il modello solo come un contenitore di dati . Mettere la logica per manipolare i dati nel ViewModel. (Questa è la logica aziendale?) Contro: la logica non è riutilizzabile.

Cosa sto provando ora: Mantenere ViewModel il più sottile possibile. Spostamento di tutta la logica nel livello del modello. Solo mantenendo la logica di presentazione nel ViewModel. Con: Rende la notifica UI davvero dolorosa Se i dati vengono modificati all'interno del livello del modello.

Quindi vi darò un esempio per renderlo più chiaro:

Scenario: strumento per rinominare i file. Classi: File: rappresentazione di ogni file; Regola: contiene la logica su come rinominare un file;

Se Im seguente approccio 1: Creazione di un ViewModel per file, regola e la vista -> RenamerViewModel. Inserimento di tutta la logica in RenamerViewModel: Contenente un elenco di FileViewModel e RuleViewModel e la logica procedurale. Facile e veloce, ma non riutilizzabile.

Se Im seguente approccio 2: Creazione di una nuova Classe Modello -> Renamer, che contiene un elenco di file, regola und Logica procedere alla interate su ogni file e applicare ogni regola. Creazione di un Viewmodel per file, regole e Renamer. Ora il RenamerViewModel contiene solo un'istanza di Renamer Model, più due ObservableCollections per racchiudere il file e l'elenco delle regole del Renamer. Ma l'intera logica si trova nel modello Renamer. Quindi, se il Renamer Model viene attivato per manipolare alcuni dati tramite chiamate di metodi, ViewModel non ha alcun indizio su cui i dati vengono manipolati. Poiché il modello non contiene alcuna notifica PropertyChange e lo eviterò. Quindi la logica aziendale e di presentazione è separata, ma ciò rende difficile la notifica all'interfaccia utente.

risposta

34

Inserire la logica di business all'interno di viewmodel è un pessimo modo di fare le cose, quindi ho intenzione di dire rapidamente mai farlo e passare alla discussione della seconda opzione.

Inserire la logica all'interno del modello è molto più ragionevole ed è un ottimo approccio di partenza. Quali sono gli svantaggi?La tua domanda dice

Quindi, se il modello Renamer è innescata a manipolare alcuni dati con il metodo chiede, il ViewModel non ha idea di quali dati vengono manipolati. Perché il modello non contiene alcuna notifica PropertyChange e io lo sostituirò con .

Bene, rendere il tuo modello implementare INotifyPropertyChanged ti permetterebbe sicuramente di passare a cose migliori. Tuttavia, è vero che a volte non è possibile farlo - ad esempio il modello può essere una classe parziale in cui le proprietà sono generate automaticamente da uno strumento e non generano notifiche di modifica. Questo è sfortunato, ma non la fine del mondo.

Se si desidera acquistare qualcosa allora qualcuno deve pagare; se non è il modello che dà tali notifiche poi si sono lasciati con solo due scelte:

  1. ViewModel sa che le operazioni sul modello (possibilmente) causano modifiche e aggiorna il suo stato dopo ogni tale operazione.
  2. Qualcun altro sa quali operazioni causano modifiche e avvisano il viewmodel di aggiornare il suo stato dopo che il modello è stato modificato.

La prima opzione è di nuovo una cattiva idea, perché in effetti sta andando di nuovo a mettere "logica di business" all'interno del ViewModel. Non così male come mettere tutto la logica di business nel viewmodel, ma ancora.

La seconda opzione è più promettente (e purtroppo ulteriori azioni da intraprendere):

  • trasferire parte la logica di business in una classe separata (un "servizio"). Il servizio implementerà tutte le operazioni aziendali che si desidera eseguire lavorando con istanze di modelli appropriate.
  • Ciò significa che il servizio sa quando le proprietà del modello possono cambiare (questo è OK: modello + servizio == business logic).
  • Il servizio fornirà notifiche sui modelli modificati a tutte le parti interessate; i tuoi viewmodels prenderanno una dipendenza dal servizio e riceveranno queste notifiche (in modo che possano sapere quando il "loro" modello è stato aggiornato).
  • Poiché le operazioni di business sono anche implementate dal servizio, questo rimane molto naturale (ad esempio quando un comando viene richiamato sul viewmodel la reazione sta chiamando un metodo appropriato sul servizio, ricorda, il viewmodel stesso non conosce il business logica).

Per ulteriori informazioni su tale implementazione vedere anche le mie risposte here e here.

+1

La parte Notification è sempre descritta come la parte ViewModel, ecco perché eviterei di farlo nel Modello. Sembra di fare la stessa cosa due volte. – JDeuker

+1

@ J.D .: certo, ma è quello o l'implementazione dei servizi. La tua chiamata. – Jon

+0

@Jon: +1 per "non farlo mai". Gli sviluppatori abituati al modello N-tier tendono a dimenticare che è OK aggiungere i riferimenti alle librerie WPF alla VM, per restituire oggetti complessi che richiedono la composizione, come un FlowDocument. –

10

Entrambi gli approcci sono validi, ma esiste un terzo approccio: implementare un servizio tra il modello ei livelli VM. Se vuoi mantenere i tuoi modelli stupidi, un servizio può fornire un intermediario indipendente dall'interfaccia utente in grado di far rispettare le regole aziendali in modo riutilizzabile.

perché il modello doesnt contenere qualsiasi notifica PropertyChange e mi eviterà che

Perché sei evitare questo? Non fraintendetemi, tendo a mantenere i miei modelli il più stupidi possibile, ma l'implementazione della notifica di modifica nel modello può a volte essere utile, e quando si esegue una dipendenza solo su System.ComponentModel. È completamente indipendente dall'interfaccia utente.

+0

Il servizio utilizza le istanze Model o ViewModel? – JDeuker

+0

@ J.D .: model - non esiste alcuna dipendenza dal livello di vista dal livello di servizio. Si può pensare al livello di servizio come ad aumentare e proteggere l'integrità del livello del modello. –

0

È inoltre possibile implementare IDataErrorInfo su entrambi: Model e ViewModel, ma eseguendo la convalida solo in Model, ciò faciliterà l'implementazione delle regole aziendali solo nel Model ...

Es:

ViewModel:

... 

private Person person; 

... 

string IDataErrorInfo.this[string propertyName] 
{ 
    get 
    { 
     string error = (person as IDataErrorInfo)[propertyName]; 
     return error; 
    } 
} 

Modello:

public class Person:INotifyPropertyChanged,IDataErrorInfo 
{ 

... 

    string IDataErrorInfo.this[string propertyName] 
    { 
     get { return this.GetValidationError(propertyName); } 
    } 

... 

    string GetValidationError(string propertyName) 
    { 
     if(propertyName == "PersonName") 
      //do the validation here returning the string error 
    } 
} 

Inoltre dare un'occhiata al modello MVCVM, im effettua la connessione ed è piuttosto bene di astrarre il business logica a una classe controller al posto del modello o del modello di vista

1

Io faccio il seguente

  1. View con XAML vista logica solo

  2. ViewModel che gestisce i gestori di click e la creazione di nuovi modelli di visualizzazione. Gestisce eventi instradati ecc.

  3. Modello che è il contenitore di dati e la logica aziendale per la convalida dei dati del modello.

  4. Servizi che popolano il modello con i dati. Ad esempio, chiama un server web, caricalo da disco, salva su disco ecc. A seconda dell'esempio, sia il mio modello che il servizio implementeranno IPropertyChanged. Oppure possono avere invece gestori di eventi.

Qualsiasi applicazione complessa ha bisogno di un altro livello. Lo chiamo model + service, view, viewmodel. Il servizio astrae la logica aziendale e prende un'istanza di modello come dipendenza o crea un modello.

Problemi correlati