Pensiero di implementazione di Model-View-Presenter

33

Sto cercando di capire come implementare il buon disaccoppiamento tra un'interfaccia utente e il modello, ma non riesco a capire esattamente dove suddividere le linee.

Ho visto Model-View-Presenter, ma non sono sicuro di come implementarlo. Ad esempio, la mia vista ha più finestre di dialogo ..

  • Dovrebbe esserci una classe View con istanze di ciascuna finestra di dialogo? In tal caso, in che modo i dialoghi dovrebbero interagire con Presenter? vale a dire. se una finestra di dialogo individuale ha bisogno di richiedere dati dal Modello tramite il Presenter, come dovrebbe la finestra di dialogo ottenere un riferimento al Presenter? Tramite un riferimento alla vista fornita durante la costruzione?
  • Stavo pensando che forse la vista dovrebbe essere una classe statica? Quindi le finestre di dialogo di GetView e ottenere il Presenter da lì ...
  • Stavo pensando di configurarlo con la proprietà della vista e del modello (a differenza della vista che ha il presentatore e il presentatore con il modello) e il presentatore che registra i callback per gli eventi nella vista, ma questo lo rende sembra molto più accoppiato (o la lingua è dipesa, almeno.)

Sto cercando di:

  1. rendilo il più disaccoppiato possibile
  2. idealmente rendono possibile accoppiare Presenter / Modello con Views of other languages (non ho fatto un sacco di roba inter-lingua, ma so che è possibile, in particolare più void(void) su cui posso attenermi, a almeno un'app C # con una libreria C ++ ...
  3. mantieni il codice pulito e semplice

Quindi ... qualche suggerimento su come devono essere gestite le interazioni?

    
posta trycatch 22.03.2011 - 16:46
fonte

4 risposte

36

Benvenuto su una pista scivolosa. A questo punto hai capito che esiste una variazione infinita di tutte le interazioni vista modello. MVC, MVP (Taligent, Dolphin, Passive View), MVVM solo per citarne alcuni.

Il pattern Model View Presenter, come la maggior parte dei pattern architettonici, è aperto a molte varietà e sperimentazioni. L'unica cosa che accomuna tutte le varianti è il ruolo del presentatore come "intermediario" tra la vista e il modello. I due più comuni sono Vista passiva e Supervisore / controllore supervisore - [ Fowler ]. Vista passiva considera l'interfaccia utente come un'interfaccia molto superficiale tra l'utente e il presentatore. Contiene pochissima logica, delegando la stessa responsabilità a un presentatore. Supervisore presentatore / controllore cerca di trarre vantaggio dall'associazione dati incorporata in molti framework UI. L'interfaccia utente gestisce la sincronizzazione dei dati ma i passaggi presentatore / controller per una logica più complessa. In entrambi i casi il modello, la vista e il presentatore formano una triade

Ci sono molti modi per farlo. È molto comune vederlo trattato trattando ogni finestra / modulo come una vista diversa. Molte volte c'è una relazione 1: 1 tra visualizzazioni e presentatori. Questa non è una regola dura e veloce. È abbastanza comune che un presentatore gestisca più viste correlate o viceversa. Tutto dipende dalla complessità della vista e dalla complessità della logica aziendale.

Per quanto riguarda il modo in cui viste e relatori ottengono un riferimento tra loro, questo viene talvolta chiamato cablaggio . Hai tre scelte:

La vista contiene un riferimento al presentatore
Una forma o un dialogo implementa una vista. Il modulo ha gestori di eventi che delgate a un relatore utilizzando le chiamate di funzione diretta:

MyForm.SomeEvent(Sender)
{
  Presenter.DoSomething(Sender.Data);
}

Poiché il relatore non ha un riferimento alla vista, la vista deve inviarlo come argomento. Il presentatore può comunicare nuovamente alla vista utilizzando le funzioni di eventi / callback che la vista deve ascoltare.

Presentatore detiene un riferimento per visualizzare
Nello scenario la vista espone le proprietà per i dati visualizzati all'utente. Il relatore ascolta gli eventi e manipola le proprietà sulla vista:

Presenter.SomeEvent(Sender)
{
  DomainObject.DoSomething(View.SomeProperty);
  View.SomeOtherProperty = DomainObject.SomeData;
}

Entrambi mantengono un riferimento l'uno all'altro formando una dipendenza circolare
Questo scenario è in realtà più facile da lavorare rispetto agli altri. La vista risponde agli eventi chiamando i metodi nel presentatore. Il relatore legge / modifica i dati dalla vista attraverso le proprietà esposte.

View.SomeEvent(Sender)
{
  Presenter.DoSomething();
}

Presenter.DoSomething()
{
  View.SomeProperty = DomainObject.Calc(View.SomeProperty);
}

Ci sono altri problemi da considerare con i pattern MVP. Ordine di creazione, durata dell'oggetto, dove avviene il cablaggio, comunicazione tra le triadi MVP ma questa risposta è già abbastanza lunga.

    
risposta data 22.03.2011 - 21:15
fonte
7

Come tutti hanno detto, ci sono dozzine di opinioni e nessuno di loro è giusto o sbagliato. Senza entrare nella miriade di modelli e concentrandoci solo su MVP, ecco alcuni suggerimenti sull'implementazione.

Tienili separati. La vista dovrebbe implementare un'interfaccia che forma il legame tra la vista e il presentatore. La vista crea un presentatore e si inietta nel presentatore e espone i metodi che offre al presentatore per interagire con la vista. La vista è responsabile dell'implementazione di questi metodi o proprietà in qualsiasi modo desideri. Generalmente hai una vista: un presentatore ma in alcuni casi puoi avere molte viste: un presentatore (web, wpf, ecc.). La chiave qui è che il presentatore non sa nulla delle implementazioni dell'interfaccia utente e interagisce solo con la vista attraverso l'interfaccia.

Ecco un esempio. Per prima cosa abbiamo una classe di visualizzazione con un metodo semplice per visualizzare un messaggio all'utente:

interface IView
{
  public void InformUser(string message);
}

Ora ecco il presentatore. Nota che il relatore accetta una IView nel suo costruttore.

class Presenter
{
  private IView _view;
  public Presenter(IView view)
  {
    _view = view;
  }
}

Ora ecco l'interfaccia utente reale. Potrebbe trattarsi di una finestra, una finestra di dialogo, una pagina Web, ecc. Non importa. Nota che il costruttore per la vista creerà il presentatore inserendosi in esso.

class View : IView
{
  private Presenter _presenter;

  public View()
  {
    _presenter = new Presenter(this);
  }

  public void InformUser(string message)
  {
    MessageBox.Show(message);
  }
}

Il presentatore non si preoccupa di come la vista implementa il metodo che fa. Per quanto ne sa il presentatore, potrebbe scrivere su un file di log e nemmeno mostrarlo all'utente.

In ogni caso, il presentatore lavora con il modello sul back-end e ad un certo punto vuole informare l'utente su cosa sta succedendo. Così ora abbiamo un metodo da qualche parte nel presentatore che chiama il messaggio InformUser delle visualizzazioni.

class Presenter
{
  public void DoSomething()
  {
    _view.InformUser("Starting model processing...");
  }
}

Questo è il punto in cui ottieni il tuo disaccoppiamento. Il relatore contiene solo un riferimento a un'implementazione di IView e non si preoccupa veramente di come è implementato.

Questa è anche un'implementazione povera perché hai un riferimento al Presenter nella vista e gli oggetti sono impostati tramite costruttori. In una soluzione più robusta, probabilmente vorrai considerare l'inversione dei contenitori di controllo (IoC) come Windsor, Ninject, ecc. Che risolverebbero l'implementazione di IView per te in fase di runtime on-demand e quindi renderla ancora più disaccoppiabile.

    
risposta data 24.03.2011 - 14:47
fonte
4

Penso che sia importante ricordare che il Controller / Presentatore è il luogo in cui si svolge realmente l'azione. L'accoppiamento nel controller è inevitabile a causa della necessità.

Il punto centrale del controller è tale che se si apporta una modifica alla vista, il modello non deve cambiare e viceversa (se il modello cambia la vista non ha a entrambi) perché il controller è ciò che traduce il modello nella vista e viceversa. Tuttavia, il Controller cambierà quando il Modello o le modifiche della Vista si comportano perché è effettivamente necessario tradurre all'interno del Controller come deve essere visualizzato il Modello su come ottenere le modifiche apportate alla Vista nella Modalità.

Il miglior esempio che posso dare è che quando scrivo un'app MVC, sono in grado non solo di avere dati nella vista della GUI, ma posso anche scrivere una routine che spinge i dati estratti dal modello in una string da mostrare nel debugger (e per estensione in un file di testo normale). Se posso prendere i dati del modello e tradurlo liberamente in testo senza modificare la vista o il modello e solo il controller, allora sono sulla strada giusta.

Detto questo, devi avere riferimenti tra i diversi componenti per far funzionare tutto. Il Controller deve conoscere la Vista per spingere i dati, la Vista deve sapere del Controller per dirlo quando è stata apportata una modifica (come quando l'Utente fa clic su "Salva" o "Nuovo ..."). Il Controller deve conoscere il Modello per estrarre i dati, ma direi che il Modello non dovrebbe sapere niente altro.

Caveat: Vengo da uno sfondo totalmente Mac, Objective-C, Cocoa che ti spinge davvero nel paradigma MVC, che tu voglia o meno.

    
risposta data 22.03.2011 - 17:02
fonte
2

In generale, vuoi che il tuo modello incapsuli tutte le interazioni con quel modello. Ad esempio, le azioni CRUD (Crea, Leggi, Aggiorna, Elimina) fanno tutti parte del modello. Lo stesso vale per i calcoli speciali. Ci sono un paio di buoni motivi per questo:

  • È più facile automatizzare i test per questo codice
  • Mantiene tutte le cose importanti in un unico posto

Nel tuo controller (app MVC), tutto ciò che stai facendo è raccogliere i modelli che devi usare nella tua vista e chiamare le funzioni appropriate sul modello. Qualsiasi modifica allo stato del modello avviene in questo livello.

La tua vista mostra semplicemente i modelli che hai preparato. Essenzialmente, la vista legge solo il modello e regola la sua uscita di conseguenza.

Mappatura del principio generale alle classi effettive

Ricorda che le tue finestre di dialogo sono viste. Se hai già una classe di dialogo, non c'è motivo di creare un'altra classe "Visualizza". Il livello Presenter lega essenzialmente il modello ai controlli nella vista. La logica aziendale e tutti i dati importanti sono memorizzati nel modello.

    
risposta data 22.03.2011 - 17:21
fonte

Leggi altre domande sui tag