¿Qué incluye el "Controlador" en "MVC"?

186

Creo que entiendo los conceptos básicos de MVC: el Modelo contiene los datos y el comportamiento de la aplicación, la Vista es responsable de mostrarlo al usuario y el Controlador se ocupa de la entrada del usuario. De lo que no estoy seguro es qué es exactamente lo que pasa en el controlador.

Digamos, por ejemplo, que tengo una aplicación bastante simple (específicamente estoy pensando en Java, pero supongo que los mismos principios se aplican en otros lugares). Organizo mi código en 3 paquetes llamados app.model, app.viewy app.controller.

Dentro del app.modelpaquete, tengo algunas clases que reflejan el comportamiento real de la aplicación. Estos extends Observabley usar setChanged()y notifyObservers()para activar las vistas para actualizar cuando sea apropiado.

El app.viewpaquete tiene una clase (o varias clases para diferentes tipos de pantalla) que usa javax.swingcomponentes para manejar la pantalla. Algunos de estos componentes necesitan retroalimentarse en el modelo. Si entiendo correctamente, la Vista no debería tener nada que ver con los comentarios, eso debería ser tratado por el Controlador.

Entonces, ¿qué pongo realmente en el controlador? ¿Pongo el public void actionPerformed(ActionEvent e)en la vista con solo una llamada a un método en el controlador? Si es así, ¿se debe hacer alguna validación, etc. en el controlador? En caso afirmativo, ¿cómo respondo los mensajes de error a la Vista? ¿Debería volver a pasar por el Modelo o el Controlador debería enviarlo directamente a la Vista?

Si la validación se realiza en la Vista, ¿qué pongo en el Controlador?

Perdón por la larga pregunta, ¡solo quería documentar mi comprensión del proceso y espero que alguien pueda aclarar este problema por mí!

Paul Walker
fuente

Respuestas:

520

En el ejemplo que sugirió, tiene razón: "el usuario hizo clic en el botón" Eliminar este elemento "en la interfaz básicamente debería llamar a la función" Eliminar "del controlador. Sin embargo, el controlador no tiene idea de cómo se ve la vista, por lo que su vista debe recopilar información como "¿en qué elemento se hizo clic?"

En forma de conversación:

Ver : "Hola, controlador, el usuario acaba de decirme que quiere que se elimine el elemento 4".
Controlador : "Hmm, después de comprobar sus credenciales, se le permite hacer eso ... Oye, modelo, quiero que obtengas el elemento 4 y hagas lo que sea que hagas para eliminarlo".
Modelo : "Artículo 4 ... lo tengo. Se ha eliminado. De vuelta a usted, Controlador".
Controlador : "Aquí, recogeré el nuevo conjunto de datos. De vuelta a usted, ver".
Ver : "Genial, mostraré el nuevo conjunto al usuario ahora".

Al final de esa sección, tiene una opción: o la vista puede hacer una solicitud por separado, "darme el conjunto de datos más reciente" y, por lo tanto, ser más puro, o el controlador devuelve implícitamente el nuevo conjunto de datos con el "eliminar "operación.

Andres Jaan Tack
fuente
90
Ese diálogo es la mejor explicación de MVC que he encontrado, ¡gracias!
Paul Walker
13
Todo bien, pero no hay nada de malo con la vista que lee directamente del modelo. "Los controladores no son la policía de datos". También hay una doctrina que dice mantener a los controladores delgados. Los View Helpers son un lugar perfecto para recopilar datos listos para ser consumidos por su vista. Uno no debería tener que despachar la pila completa del controlador para reutilizar parte de la lógica de acceso a datos. Más detalles: rmauger.co.uk/2009/03/…
Excepción e
1
Estoy de acuerdo con "Excepción e". Los datos en el modelo pueden ser actualizados por muchos eventos, no necesariamente por el controlador, y por lo tanto, en algunos diseños de MVC, la M indica a la V que los datos están sucios y la V puede actualizarse. La C no tiene ningún papel que jugar en ese caso.
Mishax
68

El problema MVCes que las personas piensan que la vista, el controlador y el modelo deben ser lo más independientes posible entre sí. No lo hacen, una vista y un controlador a menudo están entrelazados, piensan en ello como M(VC).

El controlador es el mecanismo de entrada de la interfaz de usuario, que a menudo está enredado en la vista, particularmente con las GUI. Sin embargo, la vista es salida y el controlador es entrada. Una vista a menudo puede funcionar sin un controlador correspondiente, pero un controlador suele ser mucho menos útil sin una vista. Los controladores fáciles de usar utilizan la vista para interpretar la entrada del usuario de una manera más significativa e intuitiva. Esto es lo que hace que sea difícil separar el concepto del controlador de la vista.

Piense en un robot controlado por radio en un campo de detección en una caja sellada como el modelo.

El modelo tiene que ver con el estado y las transiciones de estado sin concepto de salida (visualización) o lo que desencadena las transiciones de estado. Puedo obtener la posición del robot en el campo y el robot sabe cómo cambiar la posición (dar un paso adelante / atrás / izquierda / derecha. Fácil de visualizar sin una vista o un controlador, pero no hace nada útil

Piense en una vista sin un controlador, por ejemplo, alguien en otra habitación de la red en otra habitación observando la posición del robot mientras (x, y) coordina la transmisión por una consola de desplazamiento. Esta vista solo muestra el estado del modelo, pero este tipo no tiene controlador. Nuevamente, es fácil imaginar esta vista sin un controlador.

Piense en un controlador sin vista, por ejemplo, alguien encerrado en un armario con el controlador de radio sintonizado a la frecuencia del robot. Este controlador está enviando entradas y provoca transiciones de estado sin tener idea de lo que le están haciendo al modelo (en todo caso). Fácil de imaginar, pero no realmente útil sin algún tipo de comentarios de la vista.

La mayoría de las UI fáciles de usar coordinan la vista con el controlador para proporcionar una interfaz de usuario más intuitiva. Por ejemplo, imagine una vista / controlador con una pantalla táctil que muestra la posición actual del robot en 2-D y permite al usuario tocar el punto en la pantalla que se encuentra frente al robot. El controlador necesita detalles sobre la vista, por ejemplo, la posición y la escala de la ventana gráfica, y la posición de píxeles del punto tocado en relación con la posición de píxeles del robot en la pantalla) para interpretar esto correctamente (a diferencia del tipo encerrado en el armario con El controlador de radio).

¿Ya respondí tu pregunta? :-)

El controlador es cualquier cosa que recibe información del usuario que se utiliza para hacer que el modelo pase al estado de transición. Intente mantener la vista y el controlador separados, pero tenga en cuenta que a menudo son interdependientes entre sí, por lo que está bien si el límite entre ellos es borroso, es decir, tener la vista y el controlador como paquetes separados puede no estar tan limpiamente separados como lo haría como, pero eso está bien. Es posible que deba aceptar que el controlador no se separará limpiamente de la vista, ya que la vista es del modelo.

... ¿se debe hacer alguna validación, etc. en el controlador? Si es así, ¿cómo retroalimento los mensajes de error a la Vista? ¿Debería volver a pasar por el Modelo o el Controlador debería enviarlo directamente a la Vista?

Si la validación se realiza en la Vista, ¿qué pongo en el Controlador?

Digo que una vista vinculada y un controlador deberían interactuar libremente sin pasar por el modelo. El controlador toma la entrada del usuario y debe realizar la validación (tal vez utilizando información del modelo y / o la vista), pero si la validación falla, el controlador debe poder actualizar su vista relacionada directamente (por ejemplo, mensaje de error).

La prueba de fuego para esto es preguntarse si una vista independiente (es decir, el tipo en la otra habitación que mira la posición del robot a través de la red) debería ver algo o no como resultado del error de validación de otra persona (por ejemplo, el tipo en el armario trató de decirle al robot que saliera del campo). En general, la respuesta es no: el error de validación impidió la transición de estado. Si no hubo transición de estado (el robot no se movió), no hay necesidad de contar las otras vistas. El tipo en el armario simplemente no recibió ningún comentario de que intentó causar una transición ilegal (sin vista, mala interfaz de usuario), y nadie más necesita saber eso.

Si el tipo con la pantalla táctil intentó enviar el robot fuera del campo, recibió un mensaje agradable y fácil de usar que le pedía que no matara al robot enviándolo fuera del campo de detección, pero nuevamente, nadie más necesita saber esto.

Si otros puntos de vista no necesitan saber acerca de estos errores, entonces usted está diciendo efectivamente que las aportaciones de los usuarios y los errores resultantes son parte del modelo y toda la cosa es un poco más complicado ...

Bert F
fuente
23

Aquí hay un buen artículo sobre los conceptos básicos de MVC.

Afirma ...

Controlador: el controlador traduce las interacciones con la vista en acciones que debe realizar el modelo.

En otras palabras, su lógica de negocios. El controlador responde a las acciones realizadas por el usuario en la vista y responde. Coloque la validación aquí y seleccione la vista adecuada si la validación falla o tiene éxito (página de error, cuadro de mensaje, lo que sea).

Hay otro buen artículo en Fowler .

JP Alioto
fuente
MVP es otra opción discutida en el artículo al que hace referencia, consulte martinfowler.com/eaaDev/ModelViewPresenter.html
Jon
Gracias por los enlaces, sin duda hacen una lectura interesante.
Paul Walker
18

El patrón MVC simplemente quiere que separe la presentación (= vista) de la lógica de negocios (= modelo). La parte del controlador está ahí solo para causar confusión.

Dimitri C.
fuente
1
Exactamente, lo que siempre pensé hasta ahora, pero nunca tuve el coraje de decirle a nadie ... o tal vez no se me ocurrieron las palabras adecuadas.
user1451111
1
Model-View-Confusion
Lloviendo el
10

Hablando en términos prácticos, nunca he encontrado que el concepto de controlador sea particularmente útil. Utilizo una separación estricta de modelo / vista en mi código, pero no hay un controlador claramente definido. Parece ser una abstracción innecesaria.

Personalmente, el MVC completo parece ser el patrón de diseño de fábrica, ya que fácilmente conduce a un diseño confuso y demasiado complicado. No seas un astronauta de la arquitectura .

John Kugelman
fuente
9

Según su pregunta, tengo la impresión de que está un poco confuso sobre el papel del Modelo. El modelo está fijado en los datos asociados con la aplicación; Si la aplicación tiene una base de datos, el trabajo del modelo será hablar con ella. También manejará cualquier lógica simple asociada con esos datos; si tiene una regla que dice eso para todos los casos donde TABLE.foo == "¡Hurra!" y TABLE.bar == "¡Huzzah!" luego configure TABLE.field = "W00t!", entonces desea que el Modelo se encargue de ello.

El controlador es lo que debería manejar la mayor parte del comportamiento de la aplicación. Entonces para responder a sus preguntas:

¿Pongo el público vacío actionPerformed (ActionEvent e) en la vista con solo una llamada a un método en el controlador?

Yo diría que no. Yo diría que debería vivir en el controlador; la Vista simplemente debe alimentar los datos que provienen de la interfaz de usuario al Controlador, y dejar que el Controlador decida qué métodos deben llamarse en respuesta.

Si es así, ¿se debe hacer alguna validación, etc. en el controlador?

La mayor parte de su validación realmente debe ser realizada por el controlador; debería responder a la pregunta de si los datos son válidos o no, y si no lo es, envíe los mensajes de error apropiados a la Vista. En la práctica, puede incorporar algunas comprobaciones simples de cordura en la capa de Vista para mejorar la experiencia del usuario. (Estoy pensando principalmente en entornos web, donde es posible que desee que aparezca un mensaje de error en el momento en que el usuario presiona "Enviar" en lugar de esperar todo el envío -> proceso -> cargar el ciclo de la página antes de decirles que se equivocaron .) Sólo sé cuidadoso; no desea duplicar el esfuerzo más de lo necesario, y en muchos entornos (de nuevo, estoy pensando en la web) a menudo tiene que tratar cualquier dato que provenga de la interfaz de usuario como un paquete de suciedad mentiras hasta que

Si es así, ¿cómo retroalimento los mensajes de error a la Vista? ¿Debería volver a pasar por el Modelo o el Controlador debería enviarlo directamente a la Vista?

Debería tener un protocolo configurado donde la Vista no necesariamente sepa qué sucede después hasta que el Controlador se lo indique. ¿Qué pantalla les muestra después de que el usuario golpea ese botón? La Vista puede no saber, y el Controlador puede no saberlo hasta que vea los datos que acaba de obtener. Podría ser "Ir a esta otra pantalla, como se esperaba" o "Permanecer en esta pantalla y mostrar este mensaje de error".

En mi experiencia, la comunicación directa entre el Modelo y la Vista debería ser muy, muy limitada, y la Vista no debería alterar directamente ninguno de los datos del Modelo; ese debería ser el trabajo del controlador.

Si la validación se realiza en la Vista, ¿qué pongo en el Controlador?

Véase más arriba; La validación real debe estar en el controlador. Y espero que tenga alguna idea de lo que debería ponerse en el controlador en este momento. :-)

Vale la pena señalar que todo puede ponerse un poco borroso alrededor de los bordes; Al igual que con casi cualquier cosa tan compleja como la ingeniería de software, abundan las decisiones de juicio. Solo usa tu mejor criterio, trata de mantenerte constante dentro de esta aplicación e intenta aplicar las lecciones que aprendes al próximo proyecto.

BlairHippo
fuente
7

El controlador es realmente parte de la Vista. Su trabajo es determinar qué servicios se necesitan para cumplir con la solicitud, desarmar los valores de la vista en los objetos que requiere la interfaz de servicio, determinar la próxima vista y ordenar la respuesta en un formulario que la próxima vista pueda usar . También maneja las excepciones que se lanzan y las representa en Vistas que los usuarios pueden entender.

La capa de servicio es lo que conoce los casos de uso, las unidades de trabajo y los objetos modelo. El controlador será diferente para cada tipo de vista: no tendrá el mismo controlador para UI de escritorio, basadas en navegador, Flex o móviles. Entonces digo que es realmente parte de la interfaz de usuario.

Orientado a servicios: ahí es donde se realiza el trabajo.

duffymo
fuente
3

El controlador es principalmente para la coordinación entre la vista y el modelo.

Desafortunadamente, a veces termina mezclándose con la vista, en aplicaciones pequeñas, aunque esto no es tan malo.

Te sugiero que pongas el:

public void actionPerformed(ActionEvent e)

en el controlador Entonces su oyente de acción en su vista debería delegar al controlador.

En cuanto a la parte de validación, puede ponerla en la vista o en el controlador, personalmente creo que pertenece al controlador.

Definitivamente, recomendaría echar un vistazo a la Vista pasiva y al Presentador supervisor (que es esencialmente en lo que se divide el Presentador de vista modelo, al menos por Fowler). Ver:

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

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

Jon
fuente
3

Aquí hay una regla general que uso: si es un procedimiento que usaré específicamente para una acción en esta página, pertenece al controlador, no al modelo. El modelo debe proporcionar solo una abstracción coherente para el almacenamiento de datos.

Se me ocurrió esto después de trabajar con una aplicación web de gran tamaño escrita por desarrolladores que pensaban que se entendían MVC pero realmente no. Sus "controladores" se reducen a ocho líneas de llamada a métodos de clase estáticos que generalmente no se llaman en ningún otro lugar: - / haciendo que sus modelos sean poco más que formas de crear espacios de nombres. Refactorizar esto correctamente hace tres cosas: cambia todo el SQL a la capa de acceso a datos (también conocido como modelo), hace que el código del controlador sea un poco más detallado pero mucho más comprensible, y reduce los viejos archivos "modelo" a nada. :-)

staticsan
fuente
1

También tenga en cuenta que se puede considerar que cada widget Swing contiene los tres componentes MVC: cada uno tiene un Modelo (es decir, ButtonModel), una Vista (BasicButtonUI) y un Control (JButton en sí).

akf
fuente
1

Esencialmente tiene razón sobre lo que pone en el controlador. Es la única forma en que el Modelo debe interactuar con la Vista. La acción realizada se puede colocar en la Vista, pero la funcionalidad real se puede colocar en otra clase que actuaría como el Controlador. Si va a hacer esto, le recomiendo buscar en el patrón de Comando, que es una forma de abstraer todos los comandos que tienen el mismo receptor. Perdón por la digresión.

De todos modos, una implementación MVC adecuada solo tendrá las siguientes interacciones: Modelo -> Ver Ver -> Controlador Controlador -> Ver

El único lugar donde puede haber otra interacción es si usa un observador para actualizar la Vista, entonces la Vista deberá solicitarle al Controlador la información que necesita.

mnuzzo
fuente
0

Según tengo entendido, el controlador se traduce de acciones de interfaz de usuario a acciones de nivel de aplicación. Por ejemplo, en un videojuego, el Controlador podría traducir "movió el mouse tantos píxeles" a "quiere mirar en tal o cual dirección. En una aplicación CRUD, la traducción podría" hacer clic en tal y tal botón "para "imprime esta cosa", pero el concepto es el mismo.

David Seiler
fuente
0

Lo hacemos así, utilizando Controladores principalmente para manejar y reaccionar a las entradas / acciones impulsadas por el usuario (y _Logic para todo lo demás, excepto la vista, los datos y las cosas obvias de _Model):

(1) (respuesta, reacción: lo que la aplicación web "hace" en respuesta al usuario) Blog_Controller

-> main ()

-> handleSubmit_AddNewCustomer ()

-> verificarUsuario_HasProperAuth ()

(2) (lógica de "negocio", qué y cómo "piensa" la aplicación web) Blog_Logic

-> sanityCheck_AddNewCustomer ()

-> handleUsernameChange ()

-> sendEmail_NotifyRequestedUpdate ()

(3) (vistas, portales, cómo "aparece" la aplicación web) Blog_View

-> genWelcome ()

-> genForm_AddNewBlogEntry ()

-> genPage_DataEntryForm ()

(4) (solo objeto de datos, adquirido en _ construct () de cada Blog clase *, utilizado para mantener todos los datos de aplicaciones web / memoria juntos como un solo objeto) Blog_Meta

(5) (capa de datos básicos, lectura / escritura en bases de datos) Blog_Model

-> saveDataToMemcache ()

-> saveDataToMongo ()

-> saveDataToSql ()

-> loadData ()

A veces nos confundimos un poco sobre dónde poner un método, en la C o la L. Pero el Modelo es sólido como una roca, claro como el cristal, y dado que todos los datos en la memoria residen en la _Meta, también es obvio allí. . Nuestro mayor avance fue adoptar el uso de _Meta, por cierto, ya que esto eliminó toda la suciedad de los diversos objetos _C, _L y _Modelo, lo hizo todo mentalmente fácil de administrar, además, de un solo golpe, nos dio lo que está siendo llamado "Inyección de dependencia", o una forma de transmitir un entorno completo junto con todos los datos (cuya ventaja es la creación sencilla del entorno de "prueba").

FYA
fuente