java - observer - ¿Qué entra en el "Controlador" en "MVC"?
uml model view controller (13)
Creo que entiendo los conceptos básicos de MVC: el Modelo contiene los datos y el comportamiento de la aplicación, View es responsable de mostrarlo al usuario y el Controller trata con la entrada del usuario. De lo que no estoy seguro es exactamente qué va en el controlador.
Digamos, por ejemplo, que tengo una aplicación bastante simple (estoy pensando específicamente en Java, pero supongo que los mismos principios se aplican en otro lugar). app.model
mi código en 3 paquetes llamados app.model
, app.view
y app.controller
.
Dentro del paquete app.model
, tengo algunas clases que reflejan el comportamiento real de la aplicación. Estos extends Observable
y usan setChanged()
y notifyObservers()
para activar las vistas y actualizarlas cuando corresponda.
El paquete app.view
tiene una clase (o varias clases para diferentes tipos de pantalla) que usa componentes javax.swing
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é puse realmente en el controlador? ¿ public void actionPerformed(ActionEvent e)
el public void actionPerformed(ActionEvent e)
en la Vista con solo una llamada a un método en el Controlador? En caso afirmativo, ¿debería realizarse alguna validación, etc. en el Controlador? Si es así, ¿cómo devuelvo los mensajes de error a la Vista? ¿Debería volver a pasar por el Modelo? ¿O debería el Controlador enviarlo directamente a Ver?
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 aclararme este problema.
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 realizará el modelo.
En otras palabras, tu lógica de negocios. El controlador responde a las acciones realizadas por el usuario en la vista y responde. Ponga la validación aquí y seleccione la vista apropiada 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 .
Aquí hay una regla general que uso: si se trata de 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 grande, escrita por desarrolladores que pensaban que se les entendía MVC, pero realmente no. Sus "controladores" se reducen a ocho líneas de métodos de clases estáticas llamadas que generalmente no se llaman en ninguna otra parte: - / haciendo que sus modelos sean poco más que formas de crear espacios de nombres. Refactorizar esto correctamente hace tres cosas: desplaza todo el SQL en 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. :-)
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:
public void actionPerformed(ActionEvent e)
en el controlador. Entonces su oyente de acción en su vista debería delegar en el 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 Passive View y Supervising Presenter (que es esencialmente lo que se divide en Model View Presenter, al menos por Fowler). Ver:
http://www.martinfowler.com/eaaDev/PassiveScreen.html
http://www.martinfowler.com/eaaDev/SupervisingPresenter.html
El controlador es realmente parte de la Vista. Su trabajo es determinar qué servicio (s) se necesitan para cumplir con la solicitud, desvincular los valores de la Vista a los objetos que requiere la interfaz de servicio, determinar la siguiente Vista y ordenar la respuesta en un formulario que la próxima Vista puede usar . También maneja las excepciones que se lanzan y las convierte 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 del modelo. El controlador será diferente para cada tipo de vista: no tendrá el mismo controlador para computadoras de escritorio, navegador, Flex o UI móviles. Entonces digo que es realmente parte de la IU.
Orientada al servicio: ahí es donde se realiza el trabajo.
El patrón MVC simplemente quiere que separe la presentación (= vista) de la lógica empresarial (= modelo). La parte del controlador está allí solo para causar confusión.
El problema con MVC
es 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 - piensen en M(VC)
.
El controlador es el mecanismo de entrada de la interfaz de usuario, que a menudo se enreda en la vista, particularmente con las GUI. Sin embargo, se ve la salida y se ingresa el controlador. Una vista a menudo puede funcionar sin un controlador correspondiente, pero un controlador suele ser mucho menos útil sin una vista. Los controladores amigables usan 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 trata de transiciones de estado y estado sin ningún 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 hacer la transición (dar un paso adelante / atrás / izquierda / derecha. Fácil de visualizar sin una vista o un controlador, pero no sirve de nada
Piense en una vista sin un controlador, por ejemplo, alguien en otra habitación de la red en otra habitación viendo la posición del robot como (x, y) las coordenadas que fluyen por una consola de desplazamiento. Esta vista solo muestra el estado del modelo, pero este tipo no tiene controlador. De nuevo, 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 datos de entrada y provocando transiciones de estado sin tener idea de lo que le están haciendo al modelo (en todo caso). Fácil de visualizar, pero no realmente útil sin algún tipo de retroalimentación de la vista.
La mayoría de las interfaces de usuario 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 muestre la posición actual del robot en 2-D y le permite al usuario tocar el punto de la pantalla que se encuentra justo delante del robot. El controlador necesita detalles sobre la vista, por ejemplo, la posición y escala de la ventana gráfica, y la posición de píxel del punto tocado en relación con la posición de píxel del robot en la pantalla para interpretar esto correctamente (a diferencia del tipo encerrado en el armario con el controlador de radio).
¿Ya he respondido tu pregunta? :-)
El controlador es cualquier cosa que toma la entrada del usuario que se utiliza para causar el estado de transición del modelo. Trate de mantener la vista y el controlador separados, pero sepa que a menudo son interdependientes entre sí, por lo que está bien si el límite entre ellos es difuso, es decir, tener la vista y el controlador como paquetes separados puede no estar tan claramente separados como lo haría como, pero eso está bien. Es posible que tenga que aceptar que el controlador no estará separado de la vista como la vista del modelo.
... ¿debería hacerse alguna validación, etc. en el Controlador? Si es así, ¿cómo devuelvo los mensajes de error a la Vista? ¿Debería volver a pasar por el Modelo? ¿O debería el Controlador enviarlo directamente a Ver?
Si la validación se realiza en la Vista, ¿qué pongo en el Controlador?
Yo 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 hacer la validación (tal vez usando información del modelo y / o la vista), pero si la validación falla, el controlador debería poder actualizar su vista relacionada directamente (por ejemplo, un mensaje de error).
La prueba de fuego para esto es preguntarse si una vista independiente (es decir, el hombre de la otra habitación mirando 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 intentado decirle al robot que salga del campo). En general, la respuesta es no: el error de validación evitó la transición de estado. Si no hubo transición del estado (el robot no se movió), no hay necesidad de contar los otros puntos de vista. El chico 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 al robot fuera del campo, recibió un agradable mensaje amigable que le pedía que no matara al robot enviándolo fuera del campo de detección, pero de nuevo, nadie más necesita saber esto.
Si otras vistas necesitan saber acerca de estos errores, entonces está diciendo que las entradas del usuario y los errores resultantes son parte del modelo y todo es un poco más complicado ...
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 de "borrar" del controlador. El controlador, sin embargo, no tiene idea de cómo se ve la vista, por lo que su vista debe recopilar cierta información como "¿en qué elemento se hizo clic?"
En una forma de conversación:
Ver : "Hola, controlador, el usuario me acaba de decir que quiere que se elimine el elemento 4".
Controlador : "Hmm, habiendo verificado sus credenciales, puede hacerlo ... Oye, modelo, quiero que obtengas el elemento 4 y hagas lo que hagas para eliminarlo".
Modelo : "Artículo 4 ... lo obtuve. Se ha eliminado. De vuelta a ti, Controlador".
Controlador : "Aquí, recopilaré el nuevo conjunto de datos. Volver a usted, ver".
Ver : "Genial, mostraré el nuevo conjunto al usuario ahora".
Al final de esa sección, tiene una opción: o bien la vista puede hacer una solicitud por separado, "darme el conjunto de datos más reciente", y así ser más puro, o el controlador devuelve implícitamente el nuevo conjunto de datos con el "eliminar "operación.
En esencia, tienes razón sobre lo que colocas en el controlador. Es la única forma en que el Modelo debería interactuar con la Vista. El actionperformed 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 examinar el patrón 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 adecuada de MVC tendrá las siguientes interacciones solamente: Modelo -> Vista de Vista -> 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á solicitar al Controlador la información que necesita.
En función de su pregunta, me da la impresión de que está un poco confuso sobre el papel del Modelo. El Modelo está obsesionado con 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 esa información; si tienes una regla que dice que para todos los casos donde TABLE.foo == "¡Hurra!" y TABLE.bar == "¡Hurra!" luego configure TABLE.field = "W00t!", luego 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 public void actionPerformed (ActionEvent e) en la Vista con solo una llamada a un método en el Controller?"
Yo diría que no. Yo diría que debería vivir en el controlador; La Vista debería simplemente alimentar los datos provenientes de la interfaz de usuario al Controlador, y dejar que el Controlador decida a qué métodos debe llamarse en respuesta.
"En caso afirmativo, ¿debería hacerse alguna validación, etc. en el controlador?"
La mayor parte de su validación realmente debería ser realizada por el Controlador; debe responder a la pregunta de si los datos son válidos o no, y si no lo es, envía los mensajes de error apropiados a la Vista. En la práctica, puede incorporar algunas comprobaciones de cordura simples en la capa de Vista por el bien de mejorar la experiencia del usuario. (Estoy pensando principalmente en entornos web, donde es posible que desee tener un mensaje de error emergente en el momento en que el usuario acceda a "Enviar" en lugar de esperar todo el proceso de enviar -> procesar -> cargar 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 (una vez más, estoy pensando en la web) a menudo tiene que tratar los datos que provienen de la interfaz de usuario como un paquete de sucio inmundo miente hasta que haya confirmado que es realmente legítimo.
"De ser así, ¿cómo devuelvo los mensajes de error a la Vista? ¿Debería volver a pasar por el Modelo, o debería el Controlador enviarlo directamente a Ver?"
Debería tener algún protocolo configurado donde la Vista no necesariamente sepa qué sucede después hasta que el Controlador lo diga. ¿Qué pantalla muestra después de que el usuario golpee ese botón? Es posible que View no lo sepa y que el Controlador no lo sepa 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 debe ser muy, muy limitada, y la Vista no debe 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 debería estar en el controlador. Y con suerte ya tienes una idea de lo que debería ponerse en el Controlador. :-)
Vale la pena señalar que todo puede ponerse un poco borroso en los bordes; como ocurre con la mayoría de las cosas tan complejas como la ingeniería de software, las llamadas de juicio abundan. Solo use su mejor criterio, trate de mantenerse coherente con esta aplicación y trate de aplicar las lecciones que aprenda en el próximo proyecto.
En términos prácticos, nunca he encontrado que el concepto de controlador sea particularmente útil. Utilizo estricta separación de modelo / vista en mi código pero no hay un controlador claramente definido. Parece ser una abstracción innecesaria.
Personalmente, el MVC en toda regla parece ser el patrón de diseño de fábrica, ya que conduce fácilmente a un diseño confuso y demasiado complicado. No seas un astronauta de arquitectura .
Lo hacemos así, utilizando Controladores principalmente para manejar y reaccionar a las entradas / acciones dirigidas por el usuario (y _Logic para todo lo demás, excepto para ver, datos y cosas obvias de _Modelo):
(1) (respuesta, reacción: lo que la aplicación web "hace" en respuesta al usuario) Blog_Controller
-> main ()
-> handleSubmit_AddNewCustomer ()
-> verifyUser_HasProperAuth ()
(2) (lógica de "negocios", qué y cómo la aplicación web "piensa") 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 _constructor () de cada clase Blog *, utilizado para mantener todos los datos de webapp / inmemory juntos como un objeto) Blog_Meta
(5) (capa de datos básica, lee / escribe en DB) 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 una obviedad. . 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 _Model, hizo que todo fuera mentalmente fácil de manejar, además, de un solo golpe, nos dio lo que está siendo llamada "Dependency Injection", o una forma de pasar alrededor de un entorno completo junto con todos los datos (cuya bonificación es fácil creación del entorno de "prueba").
Según lo entiendo, el controlador traduce desde acciones de interfaz de usuario a acciones a nivel de aplicación. Por ejemplo, en un videojuego, el Controller podría traducir "movió el mouse tantos píxeles" hacia "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 "imprima esto", pero el concepto es el mismo.
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).