domingo, 16 de septiembre de 2012

Patrón (arquitectura) MVP

En el proyecto en el que estoy programando, hemos tomado de decisión de adoptar el patrón MVP, que es Modelo Vista y Presentador. Es algo muy parecido al MVC.
Para hacerse una idea de lo que es, aquí alguna referencia.

Tiene  muchas ventajas, ya que separa realmente en capas la aplicación.
Mejor esto que meter todo en la presentación, no?

Primeramente hay que hacer 4 proyectos. En este ejemplo concreto voy a emplear asp.net, pero perfectamente se puede emplear WindowsForms, Gtk#, ...

Detallo cada uno de los proyectos de los que consta la solución, para implementar la arquitectura MVP. Luego se puede modificar adaptando a las necesidades concretas de cada proyecto.

1º Vista, donde se encuentran los elementos visuales, en este caso .aspx
2º Presentadores, donde se encuentra la lógica del negocio
3º Modelos, donde está lo relacionado con los datos
4º Entidades, todo lo relacionado con las entidades de negocio y lo que viaja por las capas, pero hay que tener en cuenta de que son objetos no datatables, ni datasets.

Como se puede apreciar, la solución con los cuatro proyectos.

Me voy a centrar en un pequeño ejemplo, que es dar de alta un usuario. Comencemos pues ...

Añadimos un nuevo elemento el proyecto de vistas, que se va a llamar NuevoUsuario.aspx, que va a servir para dar de alta a los nuevos usuarios. El código de presentación es el siguiente.

<%@ Page Language="C#" Inherits="Vistas.NuevoUsuario" %>



NuevoUsuario


También se tiene que crear una interfaz, que se va a llamar IUsuarioVista, se encuentra el en proyecto de Presentadores. Define el comportamiento de la página NuevoUsuario.aspx. El código es el siguiente
using System;

namespace Presentadores
{
 public interface IUsuarioVista
 {
  string Nombre { get; } 
  
  string PrimerApellido { get; }
  
  string SegundoApellido { get; }

  string DeportePreferido { get ; }
 }
}
Como se puede observar las propiedades son cada una de las cajas de texto de la página. Esta es la forma de comunicación de datos entre la vista y el presentador. Como se ve lógico hay una entidad, que se llama Usuario, se crea en el proyecto de Entidades, su código es:
using System;

namespace Entidades
{
 public class Usuario
 {
  public string Nombre {
   get;
   set;
  }
  public string PrimerApellido {
   set;
   get;
  }
  public string SegundoApellido {
   get;
   set;
  }
  public string DeportePreferido {
   get;
   set;
  }
  
  public Usuario ()
  {
   Nombre = string.Empty;
   PrimerApellido = string.Empty;
   SegundoApellido = string.Empty;
   DeportePreferido = string.Empty;
  }
 }
}
En el proyecto (capa) Modelos, creamos la clase correspondiente, para guardar, actializar, crear y eliminar un Usuario, dentro de la base de datos. El código dependerá de la base de datos con la que se trabaje, o sistema de almacenamiento correspondiente.
using System;
using Entidades;

namespace Modelos
{
 public class UsuarioRepositorio
 {
  public UsuarioRepositorio ()
  {
  }
  public void GuardarUsuario (Usuario usuario)
  {
   /*Guarda el nuevo usuario en la base de datos*/
  }
  public void EliminarUsuario (Usuario usuario)
  {
   /*Borrar el usuario en la base de datos*/
  }

  public void ActualizarUsuario (Usuario usuario)
  {
   /*Actualiza los valores del usuario en la base de datos*/
  }
 }
}
Ahora toca la lógica del negocio, es decir programar el código del presentador, relaciionado con dar de alta un nuevo usuario. Esta capa, se tiene que encargar de conseguir los datos introducidos y guardarlos en la base de datos. Para ello, por medio de la interfaz, se pueden conseguir los valores, se construye la entidad Usuario y luego se llama a la capa de datos, pasándole el nuevo usuario, para almacenarlo.
using System;
using Modelos;
using Entidades;

namespace Presentadores
{
 public class UsuarioPresentador
 {
  private IUsuarioVista _vista;
  private UsuarioRepositorio _modelo;
  
  public UsuarioPresentador (IUsuarioVista vista)
  {
   _vista = vista;
   _modelo = new Modelos.UsuarioRepositorio();
  }
  
  public void GuardarNuevoUsuario ()
  {
   var nuevoUsuario = new Usuario ()
   {
    Nombre = _vista.Nombre,
    PrimerApellido  = _vista.PrimerApellido,
    SegundoApellido = _vista.SegundoApellido,
    DeportePreferido = _vista.DeportePreferido
   };
   _modelo.GuardarUsuario(nuevoUsuario);
  }
 }
}
Como se puede apreciar el el código anterior. Colabora con la página porque en el contructor se la han pasado, y está en el objeto _vista. Hay que decir que la página tiene que implementar la interfaz IUsuarioVista. Luego el objeto que se encarga de guardar los datos, es _modelo. Ahora el código codebehing de la página es:
using System;
using System.Web;
using System.Web.UI;
using Presentadores;

namespace Vistas
{
 public partial class NuevoUsuario : System.Web.UI.Page, IUsuarioVista
 {
  private UsuarioPresentador _usuarioPresentador;
  
  public NuevoUsuario ()
  {
   _usuarioPresentador = new UsuarioPresentador (this);
  }
 
  
  public string Nombre { 
   get { return txtNombre.Text; } 
  }

  public string PrimerApellido {
   get { return txtPrimerApellido.Text; }
  }
  public string SegundoApellido { 
   get { return txtSegundoApellido.Text; }
  }
  public string DeportePreferido { 
   get { return txtDeportePreferido.Text; }
  }
  
  
  protected void btnGuardar_click (object sender, EventArgs e)
  {
   _usuarioPresentador.GuardarNuevoUsuario (); 
  }
 }
}
Como se puede apreciar, para crear en presentador, se pasa así mismo, porque implementa la interfaz IUsuarioVista. De esta forma colaboran la capa de presentación y la capa de las vistas.

Una forma de limpia y coherente de hacer las cosas, mejor que meter todo en la página y el siguiente que se apañe.

7 comentarios:

  1. es una arquitectura de software? que tal funciona?

    ResponderEliminar
    Respuestas
    1. Hola Fernando,
      Muchas gracias por preguntar.
      Te respondo a las preguntas:
      - es una arquitectura de software?
      No es una arquitectura de software, es un patrón para poder separar claramente la lógica del, pintado de los datos en la vista.
      - que tal funciona?
      Funciona muy bien, porque se aplica el patrón de segragación de responsabilidades, entonces ya no se mezcla el código con lógica y el código del pintado de la página, por lo tanto mejora la simplicidad y el poder hacer pruebas unitarias sobre la lógica.
      Si está interesado en profundizar sobre la arquitectura de software, puedes empezar a leer "Patterns of Enterprise Application Architecture" y "Domain Driven Design"

      Eliminar
  2. Ahh bueno. Modelo coloco las consultas sql? Y los calculos de cada objeto? O se separa nuevamente?

    Tdd se aplica solo para calculos o tamb para consultas sql tamb?

    Gracias por contestarme

    ResponderEliminar
    Respuestas
    1. Respondiendo a tus preguntas:
      - Modelo coloco las consultas sql?
      Modelo son objetos que representan los conceptos con los que estas trabajando, por ejemplo: coche, banco, cuenta corriente, gasto, factura, ... Las consultas sql entrarían en la capa de persistencia

      - Y los calculos de cada objeto?
      Depende de los cálculos, por ejemplo si es cancelación de una cuenta, se podría modelar como account.Cancel() Generalmente no es bueno tener en sql lógica. Generalmente se intenta que sea lo más sencillo posible en operaciones como Find, Save, Update, Delete.

      Te recomiendo un libro que te puede ayudar: Diseño Ágil con TDD, autor: Carlos Ble Jurado

      Eliminar
  3. buenas! ¿El presentador puede recibir algún objeto de la vista, por ejemplo una tabla?

    Yo quiero hacer un programa en el que tengo que escoger una ciudad de un comboBox y al apretar un botón lo agrega en una tabla. Pero ¿como haría para agregar el objeto Ciudad en la tabla si la vista no puede ver el modelo?
    Lo que tenia pensado hacer yo es pasarle el objeto tabla al presentador y que le agregue la ciudad y me la devuelva.

    Espero tu respuesta. Desde ya Muchas Gracias!

    ResponderEliminar
  4. Hola Carlos,
    Muchas gracias por leer el artículo y escribirme.

    respondo a tus preguntas:
    - "¿El presentador puede recibir algún objeto de la vista, por ejemplo una tabla?"
    El presentador, puede recibir un objeto de la vista la vista. Pero como todo, depende de la situación.

    - "... ¿como haría para agregar el objeto Ciudad en la tabla si la vista no puede ver el modelo?"
    Como lo haría yo. En el prensentador tendría una lista, con objetos y cada objeto representa cada fila y cada propiedad del objeto representa una celda de la tabla.
    Cuando el usuario a seleccionado un valor del desplegable, la vista informa al presentador que el usuario ha realizado esa acción, ¿cómo se hace? Con eventos o también con el objeto Action de .NET. El presentador, se suscribe a la vista.
    Cuando el presentador, se ha enterado de que el usuario ha seleccionado un valor del desplegable, porque la vista se lo ha comunicado, porque el presentador se ha suscrito a un evento de la vista (en ese caso concreto sería 'elemento selecionado en el desplegable'), entonces se añade un elemento a la lista que el presentador tiene internamente y de nuevo el presentador, le dice a la vista que pinte la lista. De esta forma la lógica solo se encuentra en el presentador y la vista solo sabe de pintar e interacción con el usuario.
    El que manda es el presentador y la vista simplemente hace lo que el presentador le dice. Quien tiene los datos, es el presenter. La vista solo sabe de pintar y poco más.

    Espero que te haya sido de ayuda. Si no me he explicado lo suficiente, podemos verlo más detenidamente.

    Saludos
    Luis

    ResponderEliminar