Esa es una gran pregunta. Compartiré algunas reflexiones que tengo sobre este tema y veré qué sale de él.
Swing es más Model-> Ver que Model-> View-> Controller. El problema con Model-> View es que las aplicaciones Swing normalmente subclasan a un objeto View, y que los objetos se convierten tanto en la Vista como en el Controlador para esa vista mezclada en una sola. Las horas extras en aplicaciones grandes esto lleva a muchos problemas y código de spaghetti.
Lo que he estado haciendo durante varios años es crear un objeto separado llamado Controlador que no extienda ninguna clase de UI. Es un viejo objeto simple a este respecto. Este controlador se encargará de crear instancias de los componentes de nivel superior para la Vista, conectar los oyentes a la vista para responder al usuario, y dar la vuelta y hacer llamadas al modelo para realizar el trabajo.
La vista tendrá la subclase Swing. La Vista es responsable de responder a eventos de mouse, eventos de teclado, etc. Cualquier tipo de evento específico de Swing se maneja en la Vista. También proporcionará métodos de alto nivel para actualizar la vista que usará el controlador para devolver la actualización de la interfaz de usuario. Los modelos clásicos de columpios también están separados de la Vista, ya que su elección de componentes está muy ligada a los modelos que usará. The View también se encarga de enviar eventos de alto nivel al Controlador, y el Controlador está a cargo de responder a esos eventos de alto nivel. Estos eventos pueden ser UserEvent.ADD, UserEvent.EDIT, AuthenticationEvent.LOG_IN, AuthenticationEvent.LOG_OUT, etc. Estos eventos son eventos de aplicación y leen más como lo que un gerente de producto podría reconocer. El Controlador no responde a Mouse, ChangListener, etc. De hecho, he creado mi propio EventDispatch y marco de eventos porque Swing es tan difícil de extender y utilizar de manera efectiva. La vista funciona algo así como:
public void mouseClicked(MouseEvent evt) {
User u = getUserAt(evt.getPoint());
dispatch(new UserEvent(UserEvent.EDIT, u));
}
En mi controlador tengo métodos simples que están conectados a esos eventos. Aquí podría ser un ejemplo de uno:
@EventCallback(command = "exit")
public void exit(AppEvent evt) {
onExit();
}
@EventCallback(command = "help.about")
public void showAbout(AppEvent evt) {
audioFinderFrame.showAboutDialog(engine.getLicenseInfo());
}
@EventCallback(command = MediaSourceEvent.START_REFRESH)
public void refreshStarted(final MediaSourceEvent event) {
if(frame != null) frame.refreshMediaSource(event.getSource(), true);
}
Las anotaciones son una extensión tengo que añadir métodos de detectores de evento rápidamente a una fuente EventDisptach. Pero, el punto es que cada método en el controlador se invoca desde la vista usando eventos de alto nivel. Esto permite que el Controlador esté algo aislado de cómo se visualiza la vista. El método de inicio de sesión del Controlador no tiene que preocuparse de qué componentes componen la vista. Él solo recibe un evento y realiza el trabajo. El controlador está a cargo del flujo de la aplicación.
Como el sistema de eventos está divorciado del Swing, lo reutilizo en las capas del modelo para que el modelo pueda enviar eventos de vuelta al Controlador, y el Controlador puede retransmitir esos cambios a la IU.
El modelo y el controlador son POJO. Ellos entienden los eventos, pero eso es todo. El modelo es la lógica de la aplicación, que incluye un nivel de DAO, servicios que pueden hacer trabajos en segundo plano, cualquier capa de servicio que se comunique con el servidor y objetos que la mayoría de la gente podría decir que son DTO.No prescribo la noción de que un DTO debería ser simplemente estructuras de getter/setter simples. Permito algo de lógica porque son lo único que flota entre todas las capas. Debido a que cada capa tiene acceso a ellos, proporcionan un buen lugar para centralizar la lógica que cada capa puede reutilizar. La Vista, el Controlador y el Modelo pueden acceder a estos métodos, así que ¿por qué no ponerlos en el objeto que se mueve entre ellos?
Normalmente, esta lógica está más cerca de la lógica comercial o la lógica de mantenimiento del modelo. Tengo cuidado de acoplar sistemas de arquitectura más grandes a estos métodos. Estos métodos no van a hablar con la base de datos, o llamar a los métodos del lado del servidor para que no lleven referencias a piezas de arquitectura más grandes. Tienen todas las ventajas de los DTO: ligeras, de fácil construcción, bajas dependencias, pero aún mantienen los principios del Diseño Orientado a Objetos: encapsulación, reutilización y ocultación de información.
También comencé a usar Spring para cablear las partes del modelo con sus dependencias y dependencias que el controlador tiene en el modelo. He encontrado que este modelo funciona muy bien y es mucho más agradable que no usarlo. También es bueno tener acceso a tecnologías como las plantillas Spring JDBC y las plantillas JMS si uso esas tecnologías. Pero, es opcional.
Nunca vuelvo a usar Controladores. Los controladores son lo más específico en su sistema, y las generalidades solo los hacen más difíciles de mantener. Las generalidades pertenecen a la Vista y al Modelo porque facilitan el desarrollo. Entonces, los patrones de diseño tienden a encontrarse en esos lados, pero rara vez en el Controlador. Los controladores son simples llamadas de método hacia adelante y hacia atrás.
He descubierto que hacer esto ha hecho que construir UIs Swing sea mucho más fácil y más directo. Es menos probable que entre en bucles de eventos infinitos al escuchar y manipular dos controles a la vez. También me parece más fácil probar y romper el sistema porque gran parte de mi lógica existe fuera del alcance de Swing. Eso hace que las pruebas funcionales sean posibles sin una gran caja de herramientas que intente simular clics del mouse, etc.
No hay mucho código aquí para ilustrar lo siento, pero espero que esto ayude.