Cosa succede nel "Controller" in "MVC"?


186

Credo di comprendere i concetti di base di MVC: il Modello contiene i dati e il comportamento dell'applicazione, la Vista è responsabile della sua visualizzazione per l'utente e il Controller si occupa dell'input dell'utente. Ciò di cui non sono sicuro è esattamente ciò che accade nel controller.

Diciamo ad esempio che ho un'applicazione abbastanza semplice (sto pensando specificamente a Java, ma suppongo che gli stessi principi si applichino altrove). Organizzo il mio codice in 3 pacchetti di chiamate app.model, app.viewe app.controller.

All'interno del app.modelpacchetto, ho alcune classi che riflettono il comportamento effettivo dell'applicazione. Questi extends Observablee utilizzano setChanged()e notifyObservers()per attivare le visualizzazioni per l'aggiornamento quando appropriato.

Il app.viewpacchetto ha una classe (o più classi per diversi tipi di display) che utilizza javax.swingcomponenti per gestire il display. Alcuni di questi componenti devono essere reinseriti nel Modello. Se capisco correttamente, la vista non dovrebbe avere nulla a che fare con il feedback, che dovrebbe essere gestito dal controller.

Quindi cosa inserisco effettivamente nel controller? Metto il public void actionPerformed(ActionEvent e)nella vista con solo una chiamata a un metodo nel controller? In tal caso, è necessario eseguire una convalida ecc. Nel controller? In tal caso, come posso restituire i messaggi di errore alla vista, che dovrebbe ripassare il modello o il controller deve semplicemente inviarlo di nuovo alla vista?

Se la convalida viene eseguita nella vista, cosa inserisco nel controller?

Ci scusiamo per la lunga domanda, volevo solo documentare la mia comprensione del processo e spero che qualcuno possa chiarire questo problema per me!

Risposte:


520

Nell'esempio che hai suggerito, hai ragione: "l'utente ha fatto clic sul pulsante" elimina questo elemento "nell'interfaccia in pratica dovrebbe semplicemente chiamare la funzione" elimina "del controller. Il controller, tuttavia, non ha idea di come appare la vista, quindi la tua vista deve raccogliere alcune informazioni come "a quale elemento è stato fatto clic?"

In un modulo di conversazione:

Visualizza : "Ehi, controller, l'utente mi ha appena detto che desidera eliminare l'elemento 4".
Controller : "Hmm, dopo aver verificato le sue credenziali, è autorizzato a farlo ... Ehi, modella, voglio che tu ottenga l'articolo 4 e faccia qualunque cosa tu faccia per eliminarlo."
Modello : "Articolo 4 ... capito. È stato eliminato. Di nuovo a te, controller."
Controller : "Qui, raccoglierò il nuovo set di dati. Torna a te, visualizza."
Visualizza : "Fantastico, mostrerò ora il nuovo set all'utente."

Alla fine di quella sezione, hai un'opzione: o la vista può fare una richiesta separata, "dammi il set di dati più recente", e quindi essere più pura, oppure il controller restituisce implicitamente il nuovo set di dati con "elimina "operazione.


90
Quel dialogo è la migliore spiegazione di MVC che ho incontrato, grazie!
Paul Walker,

13
Tutto bene, ma non c'è nulla di sbagliato nella lettura della vista direttamente dal modello. "I responsabili del trattamento non sono la polizia dei dati". C'è anche una dottrina che dice di mantenere i controller sottili. Visualizza Gli helper sono il luogo perfetto per raccogliere dati pronti per essere consumati dalla vista. Non è necessario inviare lo stack completo del controller per riutilizzare un po 'di logica di accesso ai dati. Maggiori dettagli: rmauger.co.uk/2009/03/…
Eccezione e

1
Sono d'accordo con "Eccezione e". I dati nel modello possono essere aggiornati da molti eventi, non necessariamente dal controller, e quindi in alcuni progetti MVC la M segnala a V che i dati sono sporchi e che la V può aggiornarsi. La C non ha alcun ruolo da svolgere in quel caso.
Mishax,

68

Il problema MVCè che le persone pensano che la vista, il controller e il modello debbano essere il più indipendenti possibile l'uno dall'altro. Non lo fanno - una vista e un controller sono spesso intrecciati - la pensano come M(VC).

Il controller è il meccanismo di input dell'interfaccia utente, che è spesso aggrovigliato nella vista, in particolare con le GUI. Tuttavia, la visualizzazione viene emessa e il controller viene immesso. Una vista può spesso funzionare senza un controller corrispondente, ma un controller è di solito molto meno utile senza una vista. I controller intuitivi utilizzano la vista per interpretare l'input dell'utente in modo più significativo e intuitivo. Questo è ciò che rende difficile separare il concetto di controller dalla vista.

Pensa a un robot radiocomandato su un campo di rilevamento in una scatola sigillata come modello.

Il modello è incentrato sulle transizioni di stato e stato senza alcun concetto di output (display) o su ciò che sta innescando le transizioni di stato. Posso ottenere la posizione del robot sul campo e il robot sa come spostare la posizione (fare un passo avanti / indietro / sinistra / destra. Facile da immaginare senza una vista o un controller, ma non fa nulla di utile

Pensa a una vista senza controller, ad esempio qualcuno in un'altra stanza della rete in un'altra stanza che osserva la posizione del robot mentre le coordinate (x, y) scorrono lungo una console a scorrimento. Questa vista mostra solo lo stato del modello, ma questo ragazzo non ha controller. Ancora una volta, è facile immaginare questa visione senza un controller.

Pensa a un controller senza vista, ad es. Qualcuno bloccato in un armadio con il radiocomando sintonizzato sulla frequenza del robot. Questo controller sta inviando input e causando transizioni di stato senza alcuna idea di cosa stanno facendo al modello (se non altro). Facile da immaginare, ma non molto utile senza una sorta di feedback dalla vista.

La maggior parte delle interfacce utente intuitive coordina la vista con il controller per fornire un'interfaccia utente più intuitiva. Ad esempio, immagina una vista / controller con un touch-screen che mostri la posizione corrente del robot in 2-D e consenta all'utente di toccare il punto sullo schermo che si trova proprio di fronte al robot. Il controller ha bisogno di dettagli sulla vista, ad es. La posizione e la scala del viewport e la posizione in pixel del punto toccato rispetto alla posizione in pixel del robot sullo schermo) per interpretarlo correttamente (a differenza del ragazzo bloccato nell'armadio con il radiocomando).

Ho già risposto alla tua domanda? :-)

Il controller è tutto ciò che accetta input dall'utente che viene utilizzato per causare lo stato di transizione del modello. Cerca di mantenere una vista e un controller separati, ma renditi conto che sono spesso interdipendenti l'uno dall'altro, quindi va bene se il confine tra loro è sfocato, cioè avere la vista e il controller come pacchetti separati potrebbe non essere così separato come si farebbe come, ma va bene. Potrebbe essere necessario accettare che il controller non verrà separato in modo chiaro dalla vista poiché la vista proviene dal modello.

... è necessario eseguire una convalida ecc. nel controller? In tal caso, come posso restituire i messaggi di errore alla vista, che dovrebbe ripassare il modello o il controller deve semplicemente inviarlo di nuovo alla vista?

Se la convalida viene eseguita nella vista, cosa inserisco nel controller?

Dico che una vista collegata e un controller dovrebbero interagire liberamente senza passare attraverso il modello. Il controller accetta l'input dell'utente e dovrebbe eseguire la convalida (magari utilizzando le informazioni del modello e / o della vista), ma se la convalida fallisce, il controller dovrebbe essere in grado di aggiornare direttamente la vista correlata (ad es. Messaggio di errore).

Il test dell'acido per questo è di chiederti se una vista indipendente (cioè il ragazzo nell'altra stanza che guarda la posizione del robot attraverso la rete) dovrebbe vedere qualcosa a causa dell'errore di convalida di qualcun altro (ad esempio il ragazzo nell'armadio ha cercato di dire al robot di scendere dal campo). Generalmente, la risposta è no: l'errore di convalida ha impedito la transizione di stato. Se non vi è stato stato di stato (il robot non si è mosso), non è necessario dire le altre opinioni. Il ragazzo nell'armadio non ha ricevuto alcun feedback sul fatto che ha cercato di causare una transizione illegale (nessuna vista - interfaccia utente errata), e nessun altro deve saperlo.

Se il ragazzo con il touchscreen ha provato a mandare il robot fuori dal campo, ha ricevuto un messaggio di facile utilizzo che gli chiedeva di non uccidere il robot mandandolo fuori dal campo di rilevamento, ma ancora, nessun altro deve saperlo.

Se altre viste non c'è da sapere su questi errori, allora si sta effettivamente dicendo che gli input da parte dell'utente e gli eventuali errori risultanti sono parte del modello e il tutto è un po 'più complicato ...


23

Ecco un buon articolo sulle basi di MVC.

Si afferma ...

Controller: il controller traduce le interazioni con la vista in azioni che devono essere eseguite dal modello.

In altre parole, la tua logica aziendale. Il controller risponde alle azioni eseguite dall'utente nella vista e risponde. Inserisci la convalida qui e seleziona la vista appropriata se la convalida fallisce o ha esito positivo (pagina di errore, finestra di messaggio, qualunque cosa).

C'è un altro buon articolo su Fowler .


MVP è un'altra opzione discussa nell'articolo a cui fai riferimento, vedi martinfowler.com/eaaDev/ModelViewPresenter.html
Jon

Grazie per i collegamenti, rendono sicuramente interessante la lettura.
Paul Walker,

18

Il modello MVC vuole semplicemente separare la presentazione (= vista) dalla logica aziendale (= modello). La parte del controller è lì solo per causare confusione.


1
Esattamente, quello che ho sempre pensato fino ad ora, ma non ho mai avuto il coraggio di dirlo a nessuno .... o potrebbe non essere stato in grado di trovare le parole giuste.
user1451111

1
Model-View-Confusion
Raining

10

In pratica, non ho mai trovato il concetto di controller particolarmente utile. Uso una rigorosa separazione del modello / vista nel mio codice ma non esiste un controller chiaramente definito. Sembra essere un'astrazione inutile.

Personalmente, l'MVC in piena regola sembra il modello di progettazione di fabbrica in quanto porta facilmente a un design confuso e troppo complicato. Non essere un astronauta di architettura .


9

Sulla base della tua domanda, ho l'impressione che tu sia un po 'confuso sul ruolo del Modello. Il modello è fissato sui dati associati all'applicazione; se l'app ha un database, il lavoro del Modello sarà di parlargli. Gestirà anche qualsiasi logica semplice associata a tali dati; se hai una regola che dice che per tutti i casi in cui TABLE.foo == "Evviva!" e TABLE.bar == "Huzzah!" quindi impostare TABLE.field = "W00t!", quindi si desidera che il Modello se ne occupi.

Il controller è ciò che dovrebbe gestire la maggior parte del comportamento dell'applicazione. Quindi, per rispondere alle tue domande:

Metto il vuoto pubblico actionPerformed (ActionEvent e) nella visualizzazione con una semplice chiamata a un metodo nel controller?

Direi di no. Direi che dovrebbe vivere nel controller; la vista dovrebbe semplicemente alimentare i dati provenienti dall'interfaccia utente nel controller e lasciare che il controller decida quali metodi dovrebbero essere chiamati in risposta.

In tal caso, è necessario eseguire una convalida ecc. Nel controller?

La maggior parte della tua convalida dovrebbe essere effettuata dal Titolare; dovrebbe rispondere alla domanda se i dati sono validi e, in caso contrario, inviare i messaggi di errore appropriati alla vista. In pratica, è possibile incorporare alcuni semplici controlli di integrità nel livello Visualizza per migliorare l'esperienza dell'utente. (Sto pensando principalmente agli ambienti Web, in cui potresti voler far apparire un messaggio di errore nel momento in cui l'utente preme "Invia" invece di attendere l'intero ciclo di invio -> processo -> carica pagina prima di dire loro che hanno rovinato .) Solo stai attento; non vuoi duplicare lo sforzo non più del necessario e in molti ambienti (di nuovo, sto pensando al web) spesso devi trattare tutti i dati provenienti dall'interfaccia utente come un pacchetto di sporcizia sporca bugie fino a quando '

In tal caso, come posso restituire i messaggi di errore alla vista, che dovrebbe ripassare il modello o il controller deve semplicemente inviarlo di nuovo alla vista?

Dovresti impostare un protocollo in cui la vista non necessariamente sappia cosa succede dopo fino a quando il controller non glielo dice. Quale schermo mostri loro dopo che l'utente ha premuto quel pulsante? La vista potrebbe non sapere e il controller potrebbe non saperlo fino a quando non esamina i dati appena ottenuti. Potrebbe essere "Vai a questa altra schermata, come previsto" o "Resta su questa schermata e visualizza questo messaggio di errore".

Nella mia esperienza, la comunicazione diretta tra il Modello e la Vista dovrebbe essere molto, molto limitata e la Vista non dovrebbe alterare direttamente nessuno dei dati del Modello; quello dovrebbe essere il lavoro del controllore.

Se la convalida viene eseguita nella vista, cosa inserisco nel controller?

Vedi sopra; la vera convalida dovrebbe essere nel controller. E spero che tu abbia qualche idea di cosa dovrebbe essere messo nel controller ormai. :-)

Vale la pena notare che tutto può diventare un po 'sfocato attorno ai bordi; come con qualsiasi altra cosa complessa come l'ingegneria del software, le sentenze abbonderanno. Usa solo il tuo miglior giudizio, cerca di rimanere coerente all'interno di questa app e prova ad applicare le lezioni apprese al prossimo progetto.


7

Il controller fa davvero parte della vista. Il suo compito è di capire quali servizi sono necessari per soddisfare la richiesta, i valori unmarshal dalla vista in oggetti richiesti dall'interfaccia del servizio, determinare la vista successiva e riportare la risposta in un modulo utilizzabile dalla vista successiva . Gestisce anche tutte le eccezioni che vengono generate e le rende in visualizzazioni comprensibili agli utenti.

Il livello di servizio è ciò che conosce i casi d'uso, le unità di lavoro e gli oggetti modello. Il controller sarà diverso per ogni tipo di vista: non avrai lo stesso controller per UI desktop, basato su browser, Flex o mobile. Quindi dico che fa davvero parte dell'interfaccia utente.

Orientamento ai servizi: ecco dove si svolge il lavoro.


3

Il controller è principalmente per il coordinamento tra la vista e il modello.

Sfortunatamente, a volte finisce per essere mescolato con la vista - in piccole app anche se questo non è poi così male.

Ti suggerisco di mettere il:

public void actionPerformed(ActionEvent e)

nel controller. Quindi il tuo listener di azioni nella tua vista dovrebbe delegare al controller.

Per quanto riguarda la parte di convalida, puoi metterla nella vista o nel controller, personalmente penso che appartenga al controller.

Consiglio vivamente di dare un'occhiata a Passive View e Supervision Presenter (che è essenzialmente ciò che suddivide Model View Presenter - almeno da Fowler). Vedere:

http://www.martinfowler.com/eaaDev/PassiveScreen.html

http://www.martinfowler.com/eaaDev/SupervisingPresenter.html


3

Ecco una regola empirica che uso: se è una procedura che userò specificamente per un'azione in questa pagina, appartiene al controller, non al modello. Il modello dovrebbe fornire solo un'astrazione coerente alla memorizzazione dei dati.

Ho escogitato questo dopo aver lavorato con una webapp di grandi dimensioni scritta da sviluppatori che pensavano di essere capiti MVC ma in realtà no. I loro "controller" sono ridotti a otto linee di chiamata dei metodi di classe statica che di solito sono chiamati da nessun'altra parte: - / rendendo i loro modelli poco più che modi di creare spazi dei nomi. Il refactoring di questo fa correttamente tre cose: sposta tutto l'SQL nel livello di accesso ai dati (aka modello), rende il codice del controller un po 'più dettagliato ma molto più comprensibile e riduce a zero i vecchi file "modello". :-)


1

si può anche considerare che ogni widget Swing può contenere i tre componenti MVC: ognuno ha un modello (es. ButtonModel), una vista (BasicButtonUI) e un controllo (JButton stesso).


1

Hai fondamentalmente ragione su ciò che hai inserito nel controller. È l'unico modo in cui il modello dovrebbe interagire con la vista. L'azione eseguita può essere posizionata nella vista, ma la funzionalità effettiva può essere collocata in un'altra classe che fungerebbe da controller. Se hai intenzione di farlo, ti consiglio di esaminare il modello di comando, che è un modo per astrarre tutti i comandi che hanno lo stesso ricevitore. Ci scusiamo per la digressione.

Ad ogni modo, un'implementazione MVC corretta avrà solo le seguenti interazioni: Modello -> Visualizza vista -> Controller Controller -> Visualizza

L'unico luogo in cui potrebbe esserci un'altra interazione è se si utilizza un osservatore per aggiornare la vista, quindi la vista dovrà chiedere al controller le informazioni di cui ha bisogno.


0

A quanto ho capito, il Controller si traduce da azioni dell'interfaccia utente in azioni a livello di applicazione. Ad esempio, in un videogioco il Controller potrebbe tradurre "spostato il mouse in così tanti pixel" in "vuole guardare in tale e in tale direzione. In un'app CRUD, la traduzione può essere" cliccata su tale e tale pulsante "in "stampa questa cosa", ma il concetto è lo stesso.


0

Lo facciamo in questo modo, utilizzando i controller principalmente per gestire e reagire a input / azioni guidati dall'utente (e _Logic per tutto il resto, tranne vista, dati e ovvi elementi _Model):

(1) (risposta, reazione - cosa fa la webapp in risposta all'utente) Blog_Controller

-> main ()

-> handleSubmit_AddNewCustomer ()

-> verifyUser_HasProperAuth ()

(2) (logica "aziendale", cosa e come "pensa" la webapp) Blog_Logic

-> sanityCheck_AddNewCustomer ()

-> handleUsernameChange ()

-> sendEmail_NotifyRequestedUpdate ()

(3) (visualizzazioni, portali, come appare "webapp") Blog_View

-> genWelcome ()

-> genForm_AddNewBlogEntry ()

-> genPage_DataEntryForm ()

(4) (solo oggetto dati, acquisito in _ construct () di ogni classe Blog *, utilizzato per mantenere tutti i dati webapp / inmemory insieme come un oggetto) Blog_Meta

(5) (livello dati di base, lettura / scrittura su DB) Blog_Model

-> saveDataToMemcache ()

-> saveDataToMongo ()

-> saveDataToSql ()

-> loadData ()

A volte ci confondiamo un po 'su dove mettere un metodo, nella C o nella L. Ma il Modello è solido, cristallino e dato che tutti i dati in memoria risiedono nella _Meta, è un gioco da ragazzi anche lì . Il nostro più grande balzo in avanti è stato l'adozione dell'uso di _Meta, tra l'altro, poiché questo ha eliminato tutto il greggio dai vari oggetti _C, _L e _Model, ha reso tutto mentalmente facile da gestire, inoltre, in un colpo solo, ci ha dato ciò che è stato chiamato "Iniezione delle dipendenze", o un modo per passare un intero ambiente insieme a tutti i dati (il cui vantaggio è la facile creazione di un ambiente "di prova").

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.