La "C" in MVC è davvero necessaria?


38

Comprendo il ruolo del modello e la vista nel modello Model-View-Controller, ma faccio fatica a capire perché è necessario un controller.

Supponiamo che stiamo creando un programma di scacchi usando un approccio MVC; lo stato del gioco dovrebbe essere il modello e la GUI dovrebbe essere la vista. Che cos'è esattamente il controller in questo caso?

È solo una classe separata che ha tutte le funzioni che verranno chiamate quando, per esempio, fai clic su un riquadro? Perché non eseguire semplicemente tutta la logica sul modello nella vista stessa?


1
Personalmente, questo è quello che faccio . Ci possono essere casi in cui non c'è alternativa a MVC, ma non lo sopporto.
Mike Dunlavey,

10
Tre parole ... "Separazione della preoccupazione".
Travis J

4
Quasi tutti i programmi Windows prima di .net utilizzavano Doc-View senza controller. Sembra che abbia avuto relativamente successo.
Martin Beckett,

Martin, monoliti in grado di (cambiare).
Indipendente il

Ho risposto di seguito, ma aggiungerò che sì, è possibile creare un'applicazione senza classi di controller distinte, ma non sarebbe MVC. Stai assumendo "un approccio MVC", quindi sì, i controller svolgono un ruolo importante. Se scegli un paradigma che non è MVC, è possibile che tu non abbia controller.
Caleb,

Risposte:


4

Usando il tuo esempio, il Controller sarebbe ciò che ha deciso quale mossa legale o meno. Il controller comunica alla vista come disporre i pezzi sulla scacchiera all'avvio utilizzando le informazioni ricevute dal modello. Ci sono più cose che possono essere gestite dal controller, ma la chiave è pensare alla logica di business su quel livello.

Ci sono momenti in cui tutto il controller fa passare le informazioni avanti e indietro, come una pagina di iscrizione. Altre volte il controller è la parte difficile dello sviluppo perché ci sono molte cose che devono essere fatte a quel livello come far rispettare le regole o fare matematica complicata per esempio. Non dimenticare il controller!


36
"Usando il tuo esempio, il Controller sarebbe ciò che ha deciso quale mossa legale o meno". Questo è un cattivo esempio :( tale logica deve essere presente anche nel Modello. Altrimenti la tua logica viene suddivisa tra Controller e Modello.
Dime,

6
@Dime: il modello non deve contenere alcuna logica. Il controller sarà quello che tiene la logica, quindi "controllando".
Travis J

34
@TravisJ Non sei d'accordo. Controllare non significa sapere come fare un lavoro. Si tratta di controllare quegli oggetti che lo fanno. Quindi la logica per fare il lavoro sarebbe nel modello e il controller controllerebbe quale modello usare per eseguire i requisiti necessari dell'azione ecc. Troppa logica nei controller a mio avviso sarebbe la ricetta per la macchia del controller ...
dreza

25
L'intero punto di OOP è di avere bit coesivi di dati e comportamenti tenuti insieme e dichiarare incapsulati internamente. Il "modello" modella sia il comportamento che i dati.
Misko,

12
-1 Il controller non deve contenere la logica aziendale. Il modello è "l'app", contiene i dati e ha routine richiamabili per tutto ciò che può accadere nell'app; non necessariamente tutto in un file o classe. La vista visualizza lo stato del modello. Il controller collega il modello / vista e il mondo reale / input. Vuoi "pubblicare" l'app come app Web? Ho solo bisogno di un controller che gestisca HTTP e una vista basata su HTML appropriata. Desideri un'interfaccia della riga di comando per la tua app? Ho solo bisogno di un controller appropriato e visualizzare. Il modello, la logica aziendale, non cambia mai in questi casi.
Inganno il

39

Perché non eseguire semplicemente tutta la logica sul modello nella vista stessa?

Il controller è la colla che lega il modello e lo osserva insieme, ed è anche l'isolamento che li tiene separati. Il modello non dovrebbe sapere nulla della vista e viceversa (almeno nella versione di MVC di Apple). Il controller si comporta come un adattatore a due vie, traducendo le azioni dell'utente dalla vista in messaggi al modello e configurando la vista con i dati del modello.

L'uso del controller per separare il modello e la visualizzazione rende il codice più riutilizzabile, più testabile e più flessibile. Considera il tuo esempio di scacchi. Il modello includerebbe ovviamente lo stato del gioco, ma potrebbe anche contenere la logica che influenza le modifiche allo stato del gioco, come determinare se una mossa è legale e decidere quando il gioco è finito. La vista mostra una scacchiera e pezzi e invia messaggi quando un pezzo si muove, ma non sa nulla del significato dietro i pezzi, come si muove ogni pezzo, ecc. Il controller conosce sia il modello che la vista, nonché il flusso complessivo del programma. Quando l'utente preme il pulsante 'nuovo gioco', è un controller che dice al modello di creare un gioco e usa lo stato del nuovo gioco per impostare il tabellone. Se l'utente fa una mossa,

Guarda cosa ottieni mantenendo il modello e visualizza separato:

  • È possibile modificare il modello o la vista senza cambiare l'altro. Potrebbe essere necessario aggiornare il controller quando si cambia uno dei due, ma in qualche modo questo fa parte del vantaggio: le parti del programma che hanno maggiori probabilità di cambiare sono concentrate nel controller.

  • Il modello e la vista possono essere entrambi riutilizzati. Ad esempio, è possibile utilizzare la stessa vista della scacchiera con un feed RSS come modello per illustrare giochi famosi. In alternativa, è possibile utilizzare lo stesso modello e sostituire la vista con un'interfaccia basata sul Web.

  • È facile scrivere test sia per il modello sia per visualizzarli per assicurarsi che funzionino come dovrebbero.

  • Sia il modello che la vista possono spesso trarre vantaggio da parti standard: array, mappe, set, stringhe e altri contenitori di dati per il modello; pulsanti, controlli, campi di testo, viste immagine, tabelle e altri per la vista.


1
Nell'architettura MVC originale per le applicazioni desktop, le viste erano classi attive, osservavano direttamente il modello e si disconnettevano dal controller.
Kevin Cline,

Il problema con tutte le risposte è che ci sono tante interpretazioni di MVC quante sono le persone che pubblicano. E i vantaggi sopra elencati si applicano solo a un'interpretazione specifica di MVC. Se si dovesse mettere la maggior parte della logica nel controller (o modello) e avere la vista Visualizza / avviare chiamate di metodo specifiche sul controller, ciò renderebbe la combo Controller / Modello autonoma e molto riutilizzabile. Il più delle volte è necessaria una nuova visione. Non ho mai avuto bisogno di riutilizzare una vista. Anche il tuo esempio RSS potrebbe essere facilmente gestito con una nuova vista che utilizza il tuo vecchio con un livello RSS in mezzo.
Dunk il

2
@Dunk: è fondamentale separare la logica aziendale dall'interfaccia utente in modo che la logica aziendale possa essere testata direttamente.
Kevin Cline,

@Kevin: sono pienamente d'accordo, la logica di business non appartiene all'interfaccia utente. Tuttavia, non è necessario che il controller faccia parte dell'interfaccia utente. Questo è ciò che intendo con molte definizioni. Nella definizione di una persona il controller gestisce la pressione dei pulsanti mentre un'altra persona lo inserisce come parte della vista. Se la vista sa come trasformare le azioni dell'operatore (es. Pressioni di pulsanti / selezioni di elementi) in richieste di applicazioni, il Controller / Modello diventa molto riutilizzabile con quasi ogni tipo di interfaccia utente, che potrebbe includere GUI, Console o interfacce di rete.
Dunk il

1
@Dunk: suppongo che tu possa chiamare qualsiasi cosa ti piaccia un "controller", ma nell'architettura MVC, il controller dipende e quindi parte dell'interfaccia utente.
Kevin Cline,

7

Esistono molti, molti modi diversi di implementare questo modello di progettazione generale, ma l'idea di base è quella di separare le varie preoccupazioni, se necessario. MVC è una bella astrazione nel senso che:

Modello : rappresenta quei dati, qualunque cosa ciò possa significare
Visualizza : rappresenta l'interfaccia utente, qualunque cosa possa significare
Controller : rappresenta la colla che fa interagire quel modello e quella vista, qualunque cosa possa significare

È estremamente flessibile perché non specifica molto. Molte persone sprecano molta larghezza di banda discutendo i dettagli di ciò che ogni elemento potrebbe significare, quali nomi dovrebbero essere usati al posto di questi e se dovrebbero esserci davvero 3 o 2 o 4 o 5 componenti, ma a questo manca il punto certo grado.

L'idea è quella di separare i diversi "pezzi" di logica in modo che non si sovrappongano. Tieni insieme le tue cose di presentazione, mantieni le tue cose di dati insieme, mantieni le tue cose di logica insieme, mantieni le tue cose di comunicazione insieme. E così via. In una certa misura, meno queste aree di preoccupazione si sovrappongono, più facile è fare cose interessanti con loro.

Questo è tutto ciò di cui dovresti davvero preoccuparti.


3
Colla, colla, mi piace quella definizione, è così corretta: l'intero modello dovrebbe essere battezzato MVG e le persone smetterebbero di grattarsi la testa in cerca di eleganza dove non c'è nessuno da trovare.
ZJR,

1
+1 per "colla"; significa anche che è la parte più adatta ad essere eseguita in un linguaggio di scripting (poiché quelli tendono ad eccellere all'incollaggio).
Donal Fellows il

@DonalFellows Mi piace molto pensare. Qualcosa che "incolla" 2 entità disparate insieme ha bisogno di molta flessibilità che promuovono i linguaggi di scripting tipicamente debolmente (cioè JavaScript)
Zack Macomber

4

Tutte le buone risposte finora. I miei due centesimi sono che mi piace pensare al controller come principalmente costruito con domande come cosa e dove?

  • Mi è stato chiesto se un pezzo degli scacchi (vista) può essere spostato su x. È
    permesso? Non sono sicuro ma so dove e chi chiedere (il modello).
  • Qualcosa mi ha chiesto di salvare i miei dati. Come diamine posso farlo? So dove chiedere però! Come salviamo i dati, o dove sono salvati, non ne ho idea, ma quella classe Repository dovrebbe sapere. Lo inoltrerò e lo farò gestire.
  • Devo mostrare all'utente la posizione attuale del pezzo degli scacchi in cui il modello l'ha spostato. non sei sicuro di voler mostrare il pezzo come verde o giallo? Bah, a chi importa, so che esiste una vista che può gestirlo, quindi passerò i dati e loro potranno decidere come verranno mostrati.

Questi piccoli frammenti sono esempi di come sto cercando di ricordare l'astrazione e il concetto che MVC sta cercando di trasmettere. Cosa, dove e come sono i miei tre principali processi di pensiero.

Cosa e dove => Controller Come e quando => Modelli e viste

In sostanza le azioni del mio controller tendono ad essere piccole e compatte e quando le leggo tendono a sembrare talvolta una perdita di tempo. A un esame più attento, agiscono come gli uomini del segnale stradale, incanalando le varie richieste ai lavoratori appropriati ma senza svolgere il lavoro effettivo da soli.


2

Un controller potrebbe aiutare ad astrarre le interfacce sia della vista che del modello in modo che non debbano conoscersi direttamente. Meno un oggetto deve sapere, più diventa portatile e testabile dall'unità.

Ad esempio, il Modello potrebbe riprodurre un'altra istanza di se stesso tramite un controller. Oppure un controller in rete può connettere insieme gli oggetti Views di due giocatori. O potrebbe essere un test di Turing in cui nessuno sa quale.


2

Entra davvero in gioco quando hai a che fare con gestori di eventi, ma hai ancora bisogno del controller per gestire le interazioni tra la vista e il modello. Idealmente, non si desidera che la vista sappia nulla del modello. Pensaci, vuoi un jsp per effettuare direttamente tutte le chiamate al database? (A meno che non sia qualcosa di simile a una ricerca di accesso.) Si desidera che la vista esegua il rendering dei dati e non abbia alcuna logica aziendale, a meno che non sia una logica di rendering della vista, ma non la logica della logica aziendale.

In GWT, ottieni una separazione più pulita con MVP. Non vi è alcuna logica aziendale (se eseguita correttamente) nella vista. Il relatore funge da controller e la vista non è a conoscenza del modello. I dati del modello vengono semplicemente passati alla vista.


1

Document-View (ovvero vista modello) è il modello standard per la maggior parte delle app di Windows scritte in MFC, quindi deve funzionare per molti casi.


1

Comprendo il ruolo del modello e la vista nel modello Model-View-Controller, ma faccio fatica a capire perché è necessario un controller.

Ne sei sicuro? (Almeno come descritto in origine) Il punto del modello deve essere il modello di dominio. La vista dovrebbe mostrare all'utente il modello di dominio. Il controller dovrebbe mappare l'input di basso livello sul modello di alto livello parlato. Per quanto ne so, il ragionamento è qualcosa del tipo: A) un uso di alto livello dell'SRP. B) Il modello è stato considerato la parte importante dell'app, quindi tienilo lontano dalle cose importanti e che cambiano più velocemente. C) logica aziendale facilmente testabile (e in grado di eseguire script).

Pensa solo se vuoi rendere il tuo programma di scacchi utilizzabile da non vedenti, scambia la vista con una versione udibile e un controller che funziona con la tastiera. Supponi di voler aggiungere giochi per posta, aggiungi un controller che accetta il testo. Versione netta del gioco? Un controller che accetta comandi da un socket farebbe il lavoro. Aggiungi un bel rendering 3d, una nuova fantastica vista. Zero cambi di modello necessari Gli scacchi sono ancora scacchi.

Se mescoli l'input con la rappresentazione del modello, perdi quell'abilità. Improvvisamente, gli scacchi non sono scacchi, ma scacchi con un mouse che sono diversi dagli scacchi con una tastiera o una connessione di rete.


0

Penso che MVC sia stupido, forse in aree specifiche funziona bene ma personalmente anche i siti Web che scrivo non sono adatti a mvc. C'è un motivo per cui senti frontend, backend e mai database-end o qualcos'altro-end

IMO dovrebbe esserci un'API (back-end) e l'app che utilizza l'API (front-end). Immagino che potresti chiamare la richiesta GET il controller (che chiama semplicemente l'API back-end) e l'html la vista ma di solito non sento le persone che parlano della vista come html puro né modello che è API back-end.

IMO tutto dovrebbe essere una solida API. In realtà non hanno bisogno di essere solidi (come in pulito e ben costruito) ma i suoi interni dovrebbero rimanere privati ​​e l'app / frontend / al di fuori dell'API non dovrebbe mai dire la connessione al database né essere in grado di fare query non elaborate.

Ora, se il tuo codice / design comporta la colla va bene. Se nella tua partita a scacchi c'è qualche markup che puoi modificare per modificare la GUI, la GUI raccoglie i coords / input e chiama MovePiece (srcPosition, dstPostion) (che può restituire un bool o enum per dire se è una mossa valida o meno ) e ok con tutta la logica presente nel modello, quindi chiamalo MVC. Tuttavia, vorrei comunque organizzare le cose per classi e API e assicurarmi che non ci sia alcuna classe di lavelli da cucina che tocchi tutto (né qualsiasi API debba sapere di tutto).


Sei il benvenuto alla tua opinione, ma questa risposta non tenta di rispondere alla domanda del PO.
Caleb,

0

Pensa a un browser che visualizza una pagina Web statica. Il modello è l'HTML. La vista è il risultato effettivo sullo schermo.

Ora aggiungi un po 'di JavaScript. Questo è il controller. Quando l'utente fa clic su un pulsante o trascina qualcosa, l'evento viene inviato a JavaScript, decide cosa fare e modifica l'HTML (modello) sottostante e il browser / renderer visualizza tali modifiche sullo schermo (Visualizza).

Forse si fa clic su un altro pulsante, l'evento viene inviato a un gestore (Controller) e può causare l'invio di una richiesta per ulteriori dati a un servizio web. Il risultato viene quindi aggiunto all'HTML (modello).

Il Controller risponde agli eventi e controlla ciò che è nel Modello e quindi ciò che è sullo schermo / Visualizza.

Facendo un passo indietro, puoi pensare all'intero browser come alla vista e al server come al controller e ai dati come al modello. Quando l'utente fa clic su un pulsante nel browser l'evento che ha inviato al server (Controller), raccoglie le risorse come una pagina HTML (modello) e lo rimanda al browser per essere visualizzato (Visualizza)

Giù nel server, che si tratti di asp, php o java, il 'codice' (controller) riceve l'evento click e interroga un database o un repository di documenti (modello) e crea HTML. Dal punto di vista del server, il risultato di tutte le sue azioni è una vista (HTML) del suo archivio dati sottostante (modello). Ma dal punto di vista del client il risultato della sua richiesta al server è il suo modello (HTML)

Anche se confondi il tuo JavaScript nel tuo HTML o il tuo PHP nel tuo HTML, Model, View, Controller esiste ancora. Anche se pensi alla tua richiesta a un server e alla risposta dal server come una semplice strada a doppio senso, c'è ancora un Modello, una Vista e un Controller.


-2

Nella mia esperienza, in un tradizionale programma di gui desktop mvc desktop, il controller finisce spaccato nella vista. La maggior parte delle persone non si prende il tempo di scomporre una classe di controller.

il libro della banda di quattro dice:

Modelli di progettazione in Smalltalk MVC

La triade di classi Model / View / Controller (MVC) [KP88] viene utilizzata per creare interfacce utente in Smalltalk-80. Guardare i modelli di progettazione all'interno di MVC dovrebbe aiutarti a capire cosa intendiamo con il termine "modello".

MVC è costituito da tre tipi di oggetti. Il modello è l'oggetto dell'applicazione, la vista è la presentazione dello schermo e il controller definisce il modo in cui l'interfaccia utente reagisce all'input dell'utente. Prima di MVC, i design dell'interfaccia utente tendevano a raggruppare questi oggetti insieme. MVC li disaccoppia per aumentare la flessibilità e il riutilizzo.

MVC disaccoppia le viste e i modelli stabilendo un protocollo di sottoscrizione / notifica tra di loro. Una vista deve garantire che il suo aspetto rifletta lo stato del modello. Ogni volta che i dati del modello cambiano, il modello notifica le viste che dipendono da esso. In risposta, ogni vista ha l'opportunità di aggiornarsi. Questo approccio consente di collegare più viste a un modello per fornire presentazioni diverse. È inoltre possibile creare nuove viste per un modello senza riscriverlo.

Il diagramma seguente mostra un modello e tre viste. (Abbiamo lasciato fuori i controller per semplicità.) Il modello contiene alcuni valori di dati e le viste che definiscono un foglio di calcolo, un istogramma e un grafico a torta mostrano questi dati in vari modi. Il modello comunica con le sue viste quando i suoi valori cambiano e le viste comunicano con il modello per accedere a questi valori.

Preso al valore nominale, questo esempio riflette un design che disaccoppia le viste dai modelli. Ma il design è applicabile a un problema più generale: il disaccoppiamento degli oggetti in modo che le modifiche a uno possano influenzare un numero qualsiasi di altri senza che l'oggetto modificato conosca i dettagli degli altri. Questo design più generale è descritto dal modello di design Observer (pagina 293).

Un'altra caratteristica di MVC è che le viste possono essere nidificate. Ad esempio, un pannello di controllo di pulsanti potrebbe essere implementato come una vista complessa contenente viste di pulsanti nidificati. L'interfaccia utente per una finestra di ispezione oggetto può essere costituita da viste nidificate che possono essere riutilizzate in un debugger. MVC supporta le visualizzazioni nidificate con la classe CompositeView, una sottoclasse di View. Gli oggetti CompositeView si comportano esattamente come gli oggetti Visualizza; una vista composita può essere utilizzata ovunque sia possibile utilizzare una vista, ma contiene anche e gestisce viste nidificate.

Ancora una volta, potremmo pensare a questo come a un design che ci consente di trattare una vista composita proprio come trattiamo uno dei suoi componenti. Ma il design è applicabile a un problema più generale, che si verifica ogni volta che vogliamo raggruppare oggetti e trattare il gruppo come un singolo oggetto. Questo design più generale è descritto dal modello di design composito (163). Ti consente di creare una gerarchia di classi in cui alcune sottoclassi definiscono oggetti primitivi (ad es. Button) e altre classi definiscono oggetti compositi (CompositeView) che assemblano le primitive in oggetti più complessi.

MVC consente inoltre di modificare il modo in cui una vista risponde all'input dell'utente senza modificarne la presentazione visiva. Potresti voler cambiare il modo in cui risponde alla tastiera, per esempio, o fargli usare un menu a comparsa invece dei tasti di comando. MVC incapsula il meccanismo di risposta in un oggetto Controller. Esiste una gerarchia di classi di controller, che semplifica la creazione di un nuovo controller come variazione rispetto a uno esistente.

Una vista utilizza un'istanza di una sottoclasse del controller per implementare una particolare strategia di risposta; per implementare una strategia diversa, è sufficiente sostituire l'istanza con un diverso tipo di controller. È anche possibile cambiare il controller di una vista in fase di esecuzione per consentire alla vista di cambiare il modo in cui risponde all'input dell'utente. Ad esempio, una vista può essere disabilitata in modo che non accetti l'input semplicemente assegnandogli un controller che ignori gli eventi di input.

La relazione View-Controller è un esempio del modello di progettazione Strategy (315). Una strategia è un oggetto che rappresenta un algoritmo. È utile quando si desidera sostituire l'algoritmo staticamente o dinamicamente, quando si hanno molte varianti dell'algoritmo o quando l'algoritmo ha strutture di dati complesse che si desidera incapsulare.

MVC utilizza altri modelli di progettazione, ad esempio Metodo di fabbrica (107) per specificare la classe controller predefinita per una vista e Decorator (175) per aggiungere lo scorrimento a una vista. Ma le principali relazioni in MVC sono date dai modelli di progettazione Observer, Composite e Strategia.


1
Sembra che questo intero post meno i primi due paragrafi sia preso alla lettera da Design Patterns . Ho formattato quella sezione come una citazione in modo che i lettori lo capiscano - per favore modifica se ho citato paragrafi che sono tuoi.
Caleb,

1
Non sono d'accordo con la tua opinione sul fatto che "il controller finisce spaccato nella vista". Forse varia con la piattaforma e il framework che stai utilizzando, ma è molto più comune nella programmazione Cocoa e Cocoa Touch creare controller adeguati piuttosto che ometterli. Se un programmatore di Objective-C omette una delle categorie, è quasi certo di essere il modello che soffre.
Caleb,

Se si accetta che questa è l'interpretazione "corretta" di MVC, MVC non acquista assolutamente nulla. Potrebbe anche essere solo MV ed escludere la C perché ogni volta che crei una nuova vista devi anche creare un nuovo controller. Quindi qual è il punto nel prendere le cose per separarli se non per ragioni teoriche di separazione delle preoccupazioni.
Dunk,
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.