Come posso configurare MVP per una soluzione Winforms?


14

Ho usato MVP e MVC in passato e preferisco MVP in quanto controlla il flusso di esecuzione molto meglio secondo me.

Ho creato la mia infrastruttura (classi di archivi dati / repository) e li utilizzo senza problemi durante la codifica dei dati di esempio, quindi ora sto passando alla GUI e sto preparando il mio MVP.

Sezione a

  1. Ho visto MVP usare la vista come punto di ingresso, ovvero nel metodo di costruzione delle viste che crea il presentatore, che a sua volta crea il modello, collegando gli eventi secondo necessità.

  2. Ho anche visto il presentatore come il punto di ingresso, in cui vengono creati una vista, un modello e un presentatore, a questo presentatore viene quindi data una vista e un oggetto modello nel suo costruttore per collegare gli eventi.

  3. Come in 2, ma il modello non viene passato al presentatore. Invece il modello è una classe statica in cui vengono chiamati metodi e le risposte vengono restituite direttamente.

Sezione B

In termini di mantenere la vista e il modello sincronizzati che ho visto.

  1. Ogni volta che un valore nella vista viene modificato, ad es. TextChangedEvento in .Net / C #. Questo genera un DataChangedEventpassaggio nel modello, per mantenerlo sempre sincronizzato. E dove cambia il modello, ovvero un evento in background che ascolta, la vista viene aggiornata con la stessa idea di sollevare a DataChangedEvent. Quando un utente desidera eseguire il commit delle modifiche, SaveEventviene eseguito il passaggio attraverso il modello per eseguire il salvataggio. In questo caso il modello imita le azioni dei dati e dei processi della vista.

  2. Simile a # b1, tuttavia la vista non si sincronizza continuamente con il modello. Invece quando l'utente desidera eseguire il commit delle modifiche, SaveEventviene attivato e il relatore acquisisce i dettagli più recenti e li passa nel modello. in questo caso il modello non è a conoscenza dei dati delle viste fino a quando non viene richiesto di agire su di esso, nel qual caso vengono passati tutti i dettagli necessari.

Sezione C

Visualizzazione di oggetti business nella vista, ovvero un oggetto (MyClass) non dati primitivi (int, double)

  1. La vista ha campi proprietà per tutti i suoi dati che verranno visualizzati come oggetti dominio / business. Ad esempio view.Animalsespone una IEnumerable<IAnimal>proprietà, anche se la vista li elabora in nodi in un TreeView. Quindi per l'animale selezionato sarebbe esposto SelectedAnimalcome IAnimalproprietà.

  2. La vista non è a conoscenza di oggetti di dominio, espone la proprietà solo per i tipi di oggetti inclusi primitivo / framework (.Net / Java). In questo caso il presentatore passerà un oggetto adattatore l'oggetto di dominio, l'adattatore quindi tradurrà un determinato oggetto business nei controlli visibili nella vista. In questo caso l'adattatore deve avere accesso ai controlli effettivi sulla vista, non a qualsiasi vista, quindi diventa più strettamente accoppiato.

Sezione D

Visualizzazioni multiple utilizzate per creare un singolo controllo. cioè hai una vista complessa con un modello semplice come salvare oggetti di diversi tipi. Potresti avere un sistema di menu sul lato con ogni clic su un elemento che mostra i controlli appropriati.

  1. Si crea una vista enorme, che contiene tutti i singoli controlli che sono esposti tramite l'interfaccia viste.

  2. Hai diverse visualizzazioni. Hai una vista per il menu e un pannello vuoto. Questa vista crea le altre viste richieste ma non le visualizza (visibile = falso), inoltre implementa l'interfaccia per ogni vista che contiene (cioè le viste figlio) in modo che possa essere esposta a un relatore. Il pannello vuoto è riempito con altre viste ( Controls.Add(myview)) e ( (myview.visible = true). Gli eventi generati in queste viste "figlio" sono gestiti dalla vista padre che a sua volta passa l'evento al presentatore, e viceversa per fornire eventi indietro agli elementi figlio.

  3. Ogni vista, che si tratti del genitore principale o delle viste figlio minore, è collegata al proprio presentatore e modello. Puoi letteralmente rilasciare un controllo di visualizzazione in un modulo esistente e avrà la funzionalità pronta, basta un cablaggio in un presentatore dietro le quinte.

Sezione E

Se tutto avesse un'interfaccia, ora in base al modo in cui l'MVP viene eseguito negli esempi precedenti, questa risposta influirà in quanto potrebbe non essere compatibile con la compatibilità incrociata.

  1. Tutto ha un'interfaccia, il View, Presenter e Model. Ognuno di questi ha quindi ovviamente un'implementazione concreta. Anche se hai solo una visione concreta, un modello e un presentatore.

  2. La vista e il modello hanno un'interfaccia. Ciò consente alle viste e ai modelli di differire. Il presentatore crea / riceve oggetti vista e modello e serve solo a passare messaggi tra di loro.

  3. Solo la vista ha un'interfaccia. Il modello ha metodi statici e non viene creato, quindi non è necessaria un'interfaccia. Se si desidera un modello diverso, il presentatore chiama un diverso set di metodi di classe statici. Essendo statico, il Modello non ha alcun link al presentatore.

Pensieri personali

Di tutte le diverse varianti che ho presentato (la maggior parte che ho probabilmente usato in qualche forma) di cui sono sicuro che ce ne siano altre. Preferisco A3 come mantenere la logica aziendale riutilizzabile al di fuori del solo MVP, B2 per ridurre la duplicazione dei dati e meno eventi. C1 per non aggiungere in un'altra classe, sicuro che mette una piccola quantità di logica non unit testabile in una vista (come viene visualizzato un oggetto di dominio) ma questo potrebbe essere rivisto dal codice o semplicemente visualizzato nell'applicazione. Se la logica fosse complessa, accetterei una classe adattatore ma non in tutti i casi. Per la sezione D, penso che D1 crei una vista troppo grande per un esempio di menu. Ho usato D2 e ​​D3 prima. Il problema con D2 è che devi scrivere un sacco di codice per indirizzare gli eventi da e verso il presentatore alla vista figlio corretta e non è compatibile con il trascinamento / rilascio, ogni nuovo controllo necessita di più collegamenti per supportare il singolo presentatore. D3 è la mia scelta preferita, ma aggiunge ancora più classi come presentatori e modelli per gestire la vista, anche se la vista sembra essere molto semplice o non ha bisogno di essere riutilizzata. penso che una miscela di D2 e ​​D3 sia meglio basata sulle circostanze. Per quanto riguarda la sezione E, penso che tutto ciò che ha un'interfaccia potrebbe essere eccessivo, lo faccio già per oggetti di dominio / business e spesso non vedo alcun vantaggio nella "progettazione", ma aiuta a deridere gli oggetti nei test. Personalmente vedrei l'E2 come una soluzione classica, sebbene abbia visto l'E3 usato in 2 progetti su cui ho lavorato in precedenza. penso che una miscela di D2 e ​​D3 sia meglio basata sulle circostanze. Per quanto riguarda la sezione E, penso che tutto ciò che ha un'interfaccia potrebbe essere eccessivo, lo faccio già per oggetti di dominio / business e spesso non vedo alcun vantaggio nella "progettazione", ma aiuta a deridere gli oggetti nei test. Personalmente vedrei l'E2 come una soluzione classica, sebbene abbia visto l'E3 usato in 2 progetti su cui ho lavorato in precedenza. penso che una miscela di D2 e ​​D3 sia meglio basata sulle circostanze. Per quanto riguarda la sezione E, penso che tutto ciò che ha un'interfaccia potrebbe essere eccessivo, lo faccio già per oggetti di dominio / business e spesso non vedo alcun vantaggio nella "progettazione", ma aiuta a deridere gli oggetti nei test. Personalmente vedrei l'E2 come una soluzione classica, sebbene abbia visto l'E3 usato in 2 progetti su cui ho lavorato in precedenza.

Domanda

Sto implementando correttamente MVP? C'è un modo giusto di farlo?

Ho letto il lavoro di Martin Fowler che ha delle variazioni e ricordo che quando ho iniziato a fare MVC, ho capito il concetto, ma in origine non sono riuscito a capire dove si trova il punto di ingresso, tutto ha una sua funzione ma ciò che controlla e crea l'originale insieme di oggetti MVC.


2
Il motivo per cui ho posto questa domanda è che sto cercando di farlo bene quasi al primo tentativo. Preferirei seguire un MVP standard, piuttosto che creare 6 applicazioni usando varianti diverse per lo stesso modello.
JonWillis,

Perfetto ... Volevo chiederlo a lungo
The King

Risposte:


4

Molto di ciò che presenti qui è molto ragionevole e valido. Alcune delle scelte dipenderanno dai dettagli dell'applicazione e da quale "si sente" giusto. Come nella maggior parte dei casi, non ci sarà una risposta giusta. Alcune delle scelte avranno senso qui e quelle scelte potrebbero essere completamente sbagliate per l'applicazione e le circostanze successive. Senza conoscere alcuni dei dettagli dell'app, penso che tu sia sulla buona strada e hai preso delle decisioni ponderate e ponderate.

Per me, sento che il Presenter dovrebbe essere quasi sempre il punto di ingresso. Avere l'interfaccia utente come punto di ingresso mette troppa logica nell'interfaccia utente e toglie la possibilità di sostituire una nuova interfaccia utente senza grandi modifiche alla codifica. E davvero quello è il lavoro del presentatore.


Forse la domanda che dovrei porre è uno dei modi per usare MVP semplicemente sbagliato. Concordo con le variazioni per adattarsi a diversi scenari, ma ritengo che dovrebbe esserci un approccio generalmente accettato. Gli esempi di MVP sono tutti semplici esempi e quasi tutti con la stessa necessità, per modificare un oggetto dominio e salvare le modifiche. Eppure da quegli esempi con lo stesso obiettivo sono state prodotte le variazioni di cui sopra. Ho appena codificato parte di un'app usando gli eventi attivati ​​nella vista per essere gestiti nel presentatore, ma avrei potuto avere la vista con un riferimento al presentatore e chiamare direttamente i metodi.
JonWillis,

Concordo con tutti gli sforzi per semplificare la visione per giustificare che non sia testato unitariamente, quindi il presentatore dovrebbe avere il controllo. La mia unica preoccupazione per questo (pensare nella mia testa, quindi potrebbe essere sbagliato) è che diciamo che winforms / usercontrols potrebbero aver bisogno di essere collegati ad elementi genitori e mi chiedevo se fosse necessaria una logica aggiuntiva in un presentatore per scriverlo.
JonWillis,

@Jon - I modi in cui MVP hanno torto? Nel mio libro sarebbe quando la vista conosce il presentatore. Potrebbe non essere "sbagliato", nel senso che funziona, ma non sarebbe MVP, a quel punto si è trasformato in qualcos'altro. Il problema con i modelli di progettazione è che gli esempi sono sempre il più chiari e semplici possibile. Quindi quando vai a implementarli per la prima volta e il mondo reale salta in piedi e dice "hey le app reali sono molto più complicate di quell'esempio insignificante". Ecco dove inizia la tua crescita. Trova ciò che funziona per te e le circostanze dell'app.
Walter,

grazie per il consiglio. Ricordo di aver imparato MVC all'università. Suonava alla grande ma con una tela bianca ti chiedi da dove cominciare e come funziona davvero. In termini di MVP che è sbagliato, intendo dire che una qualsiasi delle idee / varianti di MVC che ho pubblicato sopra il modo sbagliato di farlo, cioè quando la vista sa come funziona il presentatore. O la vista dovrebbe avere un riferimento a un'interfaccia di un presentatore o di un tipo concreto ecc. Solo molte varianti diverse che possono funzionare tutte per lo stesso scopo.
JonWillis,

1
@Jon - Le tue variazioni sono praticamente in linea con lo spirito di MVP. Per quanto riguarda l'utilizzo di interfacce o tipi concreti, dipenderà dalle circostanze dell'app. Se l'app è piuttosto piccola, non molto complessa, forse non è necessario aggiungere interfacce. Mi piace mantenere le cose il più semplice possibile, finché non è abbastanza chiaro che l'app deve assolutamente implementare l'architettura X. Vedi questa risposta per maggiori dettagli: programmers.stackexchange.com/questions/34547/…
Walter

4

Usiamo una forma modificata di MVP sulla nostra app .NET 2.0 Winforms. I due pezzi che mancavano erano un adattatore modificato del WPF ViewModel e l'aggiunta dell'associazione dei dati. Il nostro modello specifico è MVPVM.

In quasi tutti i casi ci colleghiamo come presentatore, ad eccezione dei controlli utente personalizzati, che sono cablati in vista prima per la cordialità del progettista. Utilizziamo l'iniezione delle dipendenze, ViewModels generati dal codice, BDD per i presentatori e TDD / TED per il modello.

Le macchine virtuali sono solo un'enorme mole di proprietà che aumentano PropertyChanged quando vengono modificate. È stato molto semplice generarli per codice (e relativi test unitari di esercitazione associati). Li usiamo per leggere e scrivere su controlli interagibili dall'utente e per controllare gli stati abilitati. Il ViewModel è accoppiato con il View, poiché usiamo il databinding per dannatamente vicino a tutto il resto.

La vista a volte avrà dei metodi per realizzare cose che la VM non può fare. Questo di solito controlla la visibilità degli oggetti (WinForms può essere esigente al riguardo) e le cose che si rifiutano di essere databound. La vista espone sempre eventi come "Login" o "Riavvia", con EventArgs appropriati per agire sui comportamenti degli utenti. A meno che non abbiamo dovuto utilizzare un hack come "View.ShowLoginBox", View è completamente intercambiabile purché soddisfi i requisiti generali di progettazione.

Ci sono voluti circa 6-8 mesi per inchiodare questo schema. Ha molti pezzi, ma è molto flessibile ed estremamente potente. La nostra implementazione specifica è molto asincrona e guidata dagli eventi, che può essere solo un artefatto di altri requisiti piuttosto che un effetto collaterale del comportamento di progettazione. Ad esempio, ho aggiunto la sincronizzazione dei thread alla baseclass da cui la nostra VM eredita (che ha semplicemente esposto un metodo OnPropertyChanged per sollevare l'evento) - e adesso, ora possiamo avere presentatori e modelli multithread.


So che il tuo MVPVM potrebbe essere di interesse commerciale, ma se va bene potresti fornire un codice di esempio? In una nota a margine, dove si disegna la linea su cosa fa nel modello e cosa va nel presentatore. Ho visto il presentatore essere così semplice da gestire gli eventi di visualizzazione e chiamare semplicemente il modello, al presentatore che accede ai livelli dati per passare oggetti business a un modello, fino al modello che viene sostituito dal presentatore.
JonWillis,

Sì, lasciami finire un esempio. Sarà leggermente adattato alla nostra attività, dal momento che lo sto usando come strumento di formazione interno.
Bryan Boettcher,

Grazie, lo guarderò nel fine settimana per vedere se lo capisco
JonWillis,

@insta - il link non funziona, potresti ricaricarlo da qualche parte?
Yves Schelpe,

1
Merda l'ho appena cancellata come una settimana fa. Cifre :(
Bryan Boettcher,

2

Sto usando una versione di PureMvc che è stata modificata per .Net e quindi estesa da solo.

Ero abituato a PureMvc dall'usarlo nelle applicazioni Flex. È un tipo di framework bare bones quindi è abbastanza facile da adattare se si desidera personalizzarlo.

Mi sono preso le seguenti libertà:

  • Usando la riflessione sono stato in grado di ottenere proprietà private nel view-mediator per uscire nella classe form e afferrare il riferimento (privato) per ogni controllo che stavo mediando.
  • Usando la riflessione posso collegare automaticamente i controlli alle firme degli eventi nel mio mediatore, che sono generici, quindi accendo semplicemente il parametro mittente.
  • Normalmente, PureMvc vuole che i mediatori derivati ​​definiscano i loro interessi di notifica in una funzione che restituirà una matrice di stringhe. Poiché i miei interessi sono per lo più statici e volevo avere un modo più semplice di vedere gli interessi dei mediatori, ho fatto una modifica in modo che il mediatore potesse anche dichiarare i suoi interessi tramite un insieme di variabili membro con una firma particolare: _ _ _ <classname> _ <notification-name>. In questo modo posso vedere cosa sta succedendo usando l'albero dei membri IDE invece di guardare all'interno di una funzione.

In PureMvc, è possibile utilizzare un comando come punto di ingresso, un tipico comando di avvio imposta il modello nella misura del possibile, quindi crea il form principale, crea e registra il mediatore per e con quel modulo, quindi esegue Application.Run nel modulo.

Il mediatore del modulo sarebbe responsabile della creazione di tutti i sub-mediatori, alcuni di questi possono essere automatizzati, usando di nuovo trucchi di riflessione.

Il sistema che sto usando è compatibile con il trascinamento della selezione, se capisco il tuo significato. Il modulo effettivo è tutto creato in VS, ma la mia esperienza è solo con moduli che hanno controlli staticamente creati. Cose come le voci di menu create dinamicamente sembrano fattibili con un po 'di modifica del mediatore per quel menu o sottomenu. Il punto in cui sarebbe peloso è quando il mediatore non aveva alcun elemento radice statico su cui agganciarsi e tu sei entrato nella creazione di mediatori dinamici di "istanza".

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.