Aplicando MVC en Java

Hace unos días lei sobre los patrones de diseño y observe que hay muchos, demasiados. Pero también note que uno es usualmente usado en Java y en la web. Es el patrón de diseño MVC o Modelo Vista Controlador. Este me intereso por sus características, en el que separas los mecanismos de el programa en tres partes, de manera que obtienes una abstracción de la funcionalidad de tu/tus aplicaciones.

Estaba tan emocionado, porque como una de las ventajas de este patrón es que, para ciertos eventos o casos específicos puedes reutilizar código sin necesidad de hacer cambios, sobre todo en la funcionalidad. O como dice la teoría, en el Modelo. Dándote la capacidad de utilizarlo en diferentes aplicaciones.

No quiero meter teoría, ya que aquí hay demasiados buenos programadores, así es que nada mas vamos a el ejemplo.

Para ir agarrándole la onda a eso del MVC me hice un problema simple.
Digamos que nos encargan realizar un programa que convierta una cantidad a pesos(MXN) y a Dolares(USD).
Entonces una ves que definí el algoritmo de solución. Que no era mas que  moneda * cantidad
Me dispuse a crear las clases que implementarían el patrón.

La clase Modelo:

package mvc;
 
public class Modelo {
 
 private Double moneda;
 private Double cantidad;
 private Double resultado;
 
 public void setMoneda(Double moneda) {
  this.moneda = moneda;
 }
 public void setCantidad(Double cantidad) {
  this.cantidad = cantidad;
 }
 public Double getResultado() {
  return resultado;
 }
 public void convetirDolarAPeso() {
  resultado = cantidad * moneda;
 }
 public void convertirPesoADolar() {
  resultado = cantidad * moneda;
 }
}

La clase Vista

package mvc;
 
import java.awt.BorderLayout;
import java.awt.FlowLayout;
 
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
 
public class Vista extends JFrame{
 public JButton pesos, dolares;
 private JPanel panelB, panelR;
 public JLabel lResultado;
 public JTextField campoTexto;
 
 public Vista() {
  getContentPane().setLayout(new BorderLayout());
   
  panelB = new JPanel();
  panelB.setLayout(new FlowLayout());
   
  panelR = new JPanel();
  panelR.setLayout(new FlowLayout());
   
  pesos = new JButton("Convertir a Pesos");
  dolares = new JButton("Convertir a Dolares");
   
  lResultado = new JLabel("Resultado:");
  campoTexto = new JTextField(20);
   
  panelB.add(pesos);
  panelB.add(dolares);
   
  panelR.add(lResultado);
   
  add(campoTexto, BorderLayout.NORTH);
  add(panelB, BorderLayout.SOUTH);
  add(panelR, BorderLayout.CENTER);
 }
}

La clase Controlador:

package mvc;
 
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
import javax.swing.JFrame;
 
public class Controlador implements ActionListener {
 private Modelo modelo;
 private Vista vista;
 private Double cantidad;
 
 public Controlador(Modelo modelo, Vista vista) {
  this.modelo = modelo;
  this.vista = vista;
   
  this.vista.pesos.addActionListener(this);
  this.vista.dolares.addActionListener(this);
 }
 public void iniciarVista() {
  vista.setTitle("Conversor");
  vista.pack();
  vista.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  vista.setLocationRelativeTo(null);
  vista.setVisible(true);
 }
 public void actionPerformed(ActionEvent evento) {
  if(vista.pesos == evento.getSource()) {
   if(!"".equals(vista.campoTexto.getText())) {
    try{
     cantidad = Double.parseDouble(vista.campoTexto.getText());
     modelo.setCantidad(cantidad);
     modelo.setMoneda(13.943);
     modelo.convetirDolarAPeso();
     vista.lResultado.setText(modelo.getResultado().toString());
    }catch(NumberFormatException e) {
     vista.lResultado.setText("Introduzca una cantidad valida...");
    }
   }
  }
  else if(vista.dolares == evento.getSource()) {
   if(!"".equals(vista.campoTexto.getText())) {
    try{
     cantidad = Double.parseDouble(vista.campoTexto.getText());
     modelo.setCantidad(cantidad);
     modelo.setMoneda(0.072);
     modelo.convertirPesoADolar();
     vista.lResultado.setText(modelo.getResultado().toString());
    }catch(NumberFormatException e) {
     vista.lResultado.setText("Introduzca una cantidad valida...");
    }
   }
  }
 }
}

Y por ultimo la ejecucion del patron:

package ejecutarConversor;
 
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
 
import mvc.Controlador;
import mvc.Modelo;
import mvc.Vista;
 
public class PruebaConversor {
 public static void main(String arf[]) {
  try {
   UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
  }
  catch (UnsupportedLookAndFeelException e) {
  }
  catch (ClassNotFoundException e) {
  }
  catch (InstantiationException e) {
  }
  catch (IllegalAccessException e) {
  }
  Modelo modelo = new Modelo();
  Vista vista = new Vista();
  Controlador controlador = new Controlador(modelo, vista);
  controlador.iniciarVista();
 }
}

Dando como resultado:



Y allí finaliza mi implementación de el patrón MVC. Mas que un aporte es una pregunta: ¿La forma en que lo hice es correcta?

Para los que les interese, pueden visitar mi blog donde eh colocado esta misma entrada, pero según yo, explicándola, seria genial que entraran y dieran su opinión.

Saludos.

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de Sr. Negativo

Buen ejemplo MVC

Según yo sería bueno usar alguna interface.

Metodo.java

public interface Metodo{
 public abstract double convertirCantidad();/
}

Pero me parece buen ejemplo.

Imagen de Jose Manuel

Gracias

Gracias por comentar. La verdad no se porque no me di cuenta de que estaba usando el mismo método dos veces en ves de usar solo uno.
Habrá sido por las prisas. En el blog un cuate me dijo que estaba todo jodido el ejemplo. No se, la verdad, yo lo vi bien.

Total que por eso lo coloque aquí, para que me dijeran los errores. Incluso por tu nick esperaba una critica bastante dura pero, eres mas positivo que de lo que pensaba.

Saludos.

Imagen de Sr. Negativo

Ejemplo MVC

Incluso por tu nick esperaba una critica bastante dura pero, eres mas positivo que de lo que pensaba.

jaja es para despistar. No soy tan negativo eh.

Es buen ejemplo. Pero yo esperaba una aplicación web. :D !!

Imagen de neko069

Estás detonando tu código

Estás detonando tu código desde el controlador, con:

Modelo modelo = new Modelo();
Vista vista = new Vista();
Controlador controlador = new Controlador(modelo, vista);
controlador.iniciarVista();

Cuando legalmente, el método main() de tu aplicación, debería detonar la vista.

Tus actionListener los detonas en el controller, cuando deberían detonarse en la clase de vista

Tu clase de Modelo, creo que aqui confundiste un poco el concepto del patrón MVC con una clase que sirve de modelo.

El patrón MVC dicta que existen 3 capas (layers) en tu aplicación, la Vista, el Controlador ( que hace de comunicador entre la Vista y el Modelo) y el Modelo que representan tus clases de acceso a datos.

Las clases de modelo, así como lo estás representando generalmente representan Pojos (o beans) que sirven de transporte de datos o bien son el reflejo en objetos de tablas de las bases de datos.

Imagen de Jose Manuel

¿Entonces dentro de la vista

¿Entonces dentro de la vista ya sea en el constructor o en metodo aparte inicio el frame, para que al crear una instancia de la clase desde el main inicie la vista?

Creía que los escuchadores eran implementados desde el controlador. Eso entendí en la teoría.

Yo entendí que: en el Modelo se implementa el código encargado de llevar a cabo el objetivo de la aplicación. Que en la vista se crea la parte que interactúa con el usuario. Es la recibe los datos del usuario, los envía al controlador y este los envía al modelo para que se realicen las acciones correspondientes. Después de obtener una respuesta del Modelo, toma los datos y los envía de nuevo a la vista para que sean mostrados al usuario.
Y se espera una nueva interacción para repetir el ciclo.

Según leí existen tres tipos de implementación del MVC, yo escogí la que mas simple se me hizo, ¿Acaso no hay 3 tipos? ¿Sera falso lo que estudie? Pregunto porque, si es así sera mejor que cuando decida aprender algo nuevo verifique con mayor énfasis mis fuentes de sabiduría XD

¿Esta mal así como lo entendi?, ¿Solo me salio mal la implementación del patron?, ¿Ambas? XD

En verdad no se que tan mal lo hice, es solo que creí que estaría bien aprender por mi cuenta como se hace, no es algo que este viendo en la escuela o que me hayan encargado. Así es que si lo estoy haciendo mal de manera muy obvia no les extrañe :P

Gracias por decirme donde la estoy regando, y gracias por comentar :D
Saludos.

Imagen de bferro

Mis comentarios sobre tu MVC

Que bueno Jose Manuel que dedicas tu tiempo a aprender por tu cuenta. Es la única forma de aprender. Lo que un profesor hace es contribuir a ese aprendizaje, algo que a veces algunos estudiantes no llegan a entender.
Comento algo de MVC primero y después comento tu ejemplo.
El diseño de aplicaciones siguiendo MVC surge precisamente en el entorno de aplicaciones clientes con interfaces interactivas de usuario, por lo que es bueno (como lo has hecho) escribir un ejemplo precisamente en ese entorno. Muchos creen que es una técnica usada exclusivamente en aplicaciones Web, y quizá por eso el comentario que ya te hicieron de que esperaban una aplicación Web. Es una técnica que ya tiene más de 35 años, ideada para el lenguaje Smalltalk.
La idea de MVC es aplicar el principio de "separation of concerns" para diseñar y programar aplicaciones. La técnica distingue tres concerns presentes en cualquier aplicación:

  • El Modelo: la lógica de negocio que implementa las reglas y procesos de negocio que tienen acceso a los datos y que también los actualizan. El modelo entonces tiene un componente activo (el código para la lógica de la aplicación) y un componente pasivo (los datos que en esencia representan el estado de la aplicación).
  • La Vista: La parte encargada de visualizar el contenido del modelo (el estado de la aplicación), especificando la forma en que debe presentarse ese contenido, y cambiar acorde con los cambios que se producen en el modelo.
    Para que la vista presente los cambios en el modelo, hay que resolver una de dos cosas: que la vista se "registre" con el modelo para que los cambios en éste sean reflejados en la vista, o que la vista se responsabilice de consultar el modelo cuando se requiera los datos más actualizados. La primera forma la conocemos como modelo push y la segunda como modelo pull.

  • El Controladorr: Su tarea es "traducir" las interacciones del usuario con la vista, en acciones que el modelo tiene que realizar (código de la lógica de negocio). Esas acciones casi siempre modifican el contenido del modelo y en ocasiones generan una nueva vista lo que es más común en aplicaciones Web que en aplicaciones standalone.

¿Cuáles son las responsabilidades de cada parte:
Modelo

  • Expone la funcionalidad de la aplicación. En algunas aplicaciones "complejas", esto se puede hacer usando otros patrones como Delegados de Negocio, Fachadas de Negocio, etc.
  • Notifica a la vista de los cambios que se producen en su contenido
  • Encapsula el estado de la aplicación y permite que ese estado (contenido del modelo) se consulte por la vista
  • Notifica a las vistas que se han registrado en el modelo de los cambios en su contenido. Esta notificación puede "acompañarse" del propio contenido si usamos push, o simplemente una notificación si usamos pull.

Vista

  • Visualiza ("renders") el contenido del modelo
  • Jala el contenido del modelo cuando este último le notifica que cambió (si usamos pull) y se actualiza acorde con ese nuevo contenido
  • Envía las acciones del usuario al controlador. En aplicaciones con GUI, las acciones del usuario se realizan sobre los controles de la interfaz gráfica; en aplicaciones Web esas acciones se envían al controlador mediante los métodos del protocolo HTTP

Controlador

  • Define el comportamiento de la aplicación, mapeando las acciones del usuario a los diferentes componentes de negocio que son partes del modelo.
  • Selecciona la vista que corresponde al caso de uso invocado por el usuario

¿Cómo interaccionan entre ellos, la Vista, el Controlador y el Modelo?
¿Qué variantes existen y cómo las responsabilidades de una parte se le pueden ceder a otra?

Continúo en el siguiente post .........

SIn profundizar mucho en el

SIn profundizar mucho en el código ni en la explicación y con solo darle un scrolla'zo te diré que

public class Vista extends JFrame{

No es necesario, no estás en realidad creando una subclase de JFrame, lo que estás haciendo es usarla, así que no heredes de él, mejor crea un atributo de instancia ( variable privada pues ) y listo.

Imagen de bferro

Sigo con MVC

Las interacciones entre la Vista, el Controlador y el Modelo, y las responsabilidades de cada una de estas partes, se pueden diseñar y programar de formas diferentes, sin salirnos de la esencia de MVC, dando como resultado "variantes" de este patrón o estilo.
Cualquier variante que se utilice debe buscar el mayor desacoplamiento entre las tres partes. El Modelo encapsula los componentes de la lógica de negocio y es muy probable que esos componentes puedan ser reutilizados para resolver la funcionalidad de diferentes aplicaciones, por lo que desacoplar al máximo el modelo de la vista y el controlador es muy conveniente.
No hay ninguna dificultad para desacoplar el modelo del controlador. Es el controlador el que conoce del modelo y no al revés.
La dependencia que puede tener el Modelo de la Vista depende si usamos la técnica de push o la técnica de pull. Esta dependencia es la que aparece en el Patrón Observador.
Necesitamos registrar a las vistas como observadoras del modelo, para que este último pueda notificar de cambios en su contenido a las vistas interesadas y "enviar" esos cambios si usamos push o permitir su consulta si usamos pull. El API para "consultar" el modelo depende de que técnica se seleccione.
Un escenario posible de interacciones es el siguiente:

  1. La vista se registra como "listener" del modelo. Podemos añadir un atributo de tipojavax.beans.PropertyChangeSupport al Modelo y usar los métodos de esa clase para registrar y remover listeners y para disparar los eventos cuando alguna de las propiedades del Modelo cambian, notificando el cambio a las vistas.
  2. El controlador se asocia a la vista para poder mapear las acciones del usuario a llamadas a métodos de la lógica de la aplicación. La vista entonces "conoce" al controlador y en los métodos de los listeners asociados a los controles de la interfaz gráfica, le pide al controlador la ejecución del caso de uso que corresponde a la acción del usuario. Recordemos que el Controlador define la funcionalidad de la aplicación.
  3. El Controlador llama los métodos correspondientes de los componentes de la lógica de negocio, casi siempre mediante una fachada y ellos modifican el estado del Modelo.
  4. El Modelo notifica de los cambios a las vistas registradas, las cuales reciben (push) o jalan (pull) los cambios del modelo.

Una variante de MVC: Que el Controlador se encargue de notificar a las vistas de cambios en el modelo
Varios marcos de trabajo diseñan MVC modificando la "posición" del Controlador en la triada. Ubican al Controlador como mediador de la Vista y el Modelo, eliminando la comunicación entre Vista y Modelo y asignando al Controlador la responsabilidad de actualizar la vista cuando el contenido del Modelo cambia.
En este caso, el Controlador mantiene una lista de las vistas y modelos registrados y se registra como listener de los cambios en las propiedades de los modelos. Una vez que recibe cambios en los contenidos de los modelos, notifica a las vistas registradas de esos cambios.
Con esta variante, se desacopla el Modelo de la Vista, otorgando al controlador un mayor control sobre las propiedades de interés del Modelo para las vistas registradas.
Sigo en el siguiente post con los comentarios al código de Jose Manuel

Imagen de juanluis

Me parece interesante su

Me parece interesante su post, pero me parece que esa modalidad MVC se aplica mucho cuando uno programa en IDE como Eclipse o netbeans, pero digo que es mas extenso cuando tu trabajas con BlueJ que es un entorno menos sofisticado y no ofrece ningun entorno grafico... En fin lo importante es que deja claro sobre la concepcion de MVC.

Imagen de bferro

No tiene nada que ver con el IDE

Lo que he comentado sobre MVC no tiene nada que ver con el IDE ni con un lenguaje en particular. Por supuesto que tiene que ver con el soporte que un lenguaje tenga para el diseño y programación de cliente GUI y el soporte para la generación y propagación de eventos.

Imagen de Shadonwk

Muy buena explicación +1

Muy buena explicación +1

Imagen de Jose Manuel

Estaba esperando que el señor

Estaba esperando que el señor bferro terminara su explicación pero, seguro que no ha podido. Mientras decirle que quede impresionado con su explicación. Me siento orgulloso de ser parte de la comunidad, además de corregirme y ayudarme. Me enseñan.

Por cierto Oscar, no entendí mucho tu comentario, me refiero así es una corrección en la forma de aplicar el modelo o una corrección en la forma de usar la clase JFrame.

Por ahora me daré el lujo de memorizar y entender la explicacion de bferro y ya luego regresaría con el programa corregido y haber que les parece.
Muchas gracias

"Por cierto Oscar, no entendí

"Por cierto Oscar, no entendí mucho tu comentario, me refiero así es una corrección en la forma de aplicar el modelo o una corrección en la forma de usar la clase JFrame."

En el uso de JFrame.

La herencia en la forma más alta de acomplamiento ( no quiere decir que eso sea ni bueno ni malo ) y a veces es necesaria, en tu caso no, porque no esta creando una versión especializada de un frame, más bien lo estás utilizando. Luego entonces, no hace falta heredar de él, basta conque lo uses y listo:

Ejemplo:

Antes

class Application extends JFrame {
    public static void main( String ... args )  {
        JFrame a = new Application(); // meh...
     }
     // etc. ...
}

Después

class Application {
 
    public static void main( String .. args ) {
        JFrame a = new JFrame();
    }
}

Sin necesidad de heredar.

Si necesitas acceder a la ventana para realizar alguna operación, declarala como variable de instancia:

class Application  {
    private JFrame frame;
    ....
    public static void main( String ... args ) {
        Application a = new Application();
        a.muestraUsuariosNosqueBlahblah();
    }
   private void muestraUsuariosNosqueBlahblah() {  
            frame.setVisible( ! frame.isVisible() ) ;  // muestralo , ocultalo. etc.
    }
}

:)

Imagen de chicowed

Herencia

Hey @Oscar yo he visto mucho programas implementados como extends JFrame, implements Iterable, implements Runnable etc en la declaración de la Clase, mi pregunta es todos estos libros y/o fuentes están mal, me queda claro la parte de la herencia y de las interfaces que son como se usan etc, ¿pero porque lo hacen muchos programadores asi, al final el código compila?
¿Que problemas o desventajas tiene heredar o implementar clases del API de Java en la declaración de la Clase?

Imagen de chicowed

Re: Herencia

Como dices no se esta creando una versión especializada de un frame y me queda claro porque la Herencia es precisamente para eso utilizar una clase padre para que las Clases hijas hereden de esta y puedan compartir atributos y/o métodos, así como también hacer una clase especializada.

Imagen de ezamudio

eh?

Implementar Runnable es indispensable cuando quieres que ese código sea ejecutado en un hilo dedicado, o en un ThreadPool.

A lo que Oscar se refiere es que ese patrón de extender JFrame no es lo ideal, sin embargo es muy común.

Y eso de "al final el código compila"... no pos sí. De eso a que haga lo que tiene que hacer, y que lo haga bien, hay una diferencia abismal.