El patrón Modelo Vista Presentador es una derivación del patrón MVC. Lo que se busca con este patrón es un desacople mayor entre la tecnología de interfaz d usuario y la lógica de la aplicación.
La implementación del patrón MVP presenta la siguiente estructura:
Entendemos entonces que el Modelo es la modelo de negocio como tal, la vista será nuestra tecnología de interfaz de usuario y el presentador será el encargado de desacoplar la comunicación entre el Modelo y la Vista. Un detalle importante con la implementación de este patrón es que será tal el desacople entre tecnología de interfaz de usuario y lógica, que se implementa una interface de por medio, es decir, la vista solo conocerá la definición del presentador para acudir y consumir sus métodos, pero nunca existirá un pasaje de información entre ellos debido al contrato que se declara entre la tecnología de interfaz de usuario y la interface (IVista).
Después de comprender las bases de lo que es el patrón MVP, veamos cómo integrar esto con nuestras aplicaciones. El ejemplo que haremos será muy sencillo, una aplicación que sume dos números, sé que es demasiado sencillo, pero en la complejidad del modelo no está el poder de MVP, está en la mantenibilidad del código y en el nivel de desacoplamiento que se puede llegar a lograr entre tecnología de interfaz de usuario y la lógica de la aplicación.
Para este ejemplo crearemos una solución en blanco de Visual Studio:
Sobre esta solución agregamos un nuevo proyecto del tipo librería de clases llamada Modelo y a este le agregaremos una clase llamada ModeloOperaciones
, que expondrá un método público del tipo double
llamado Sumar
, así:
public class ModeloOperaciones { public double Sumar(double num1, double num2) { return num1 + num2; } }
Compilamos para que se genere el ensamblado de esta librería que hemos creado.
A la solución también le agregaremos un nuevo proyecto del tipo librería de clases llamado Presentador
, sobre este agregamos una clase llamada PresentadorOperaciones
y expondrá dos métodos del tipo void
, uno llamado IniciarVista()
y el otro llamado ActualizarVista()
. Sobre este mismo proyecto agregaremos un elemento del tipo Interface
y lo llamaremos IVistaOperaciones
.
Sobre la interface IVista
definiremos las propiedades que deberán ser implementadas por las tecnologías de interfaz de usuario, en nuestro caso expondremos las siguientes propiedades:
public interface IVistaOperaciones { double Num1 { get; set; } double Num2 { get; set; } double Resultado { set; } }
Como sabemos, el presentador será el único que conozca de la existencia de un modelo y una interfaz de usuario, aunque de hecho el presentador no sabe que tecnología es realmente, pues el trabajará con su contrato el IVista
, por eso necesitamos ahora agregar una referencia a nuestro proyecto de Modelo y escribir el método constructor de esta clase para inicializar el objeto de la interfaz (recuerden que no se pueden crear instancias de una interfaz). El código de nuestra clase nos queda así:
public class PresentadorOperaciones { private readonly IVistaOperaciones _vista; private ModeloOperaciones _modelo; public PresentadorOperaciones(IVistaOperaciones vista) { _vista = vista; _modelo = new ModeloOperaciones(); } public void IniciarVista() { _vista.Num1 = 0; _vista.Num2 = 0; _vista.Resultado = 0; } public void ActualizarVista() { _vista.Resultado = _modelo.Sumar(_vista.Num1, _vista.Num2); } }
Con esto ya tenemos todo el «cascaron» de lo que debemos consumir, ahora construyamos un «consumidor». Sobre esta solución agreguemos ahora un proyecto del tipo Windows Forms y sobre este agreguemos dos TextBox
, un botón y un Label
, así:
A este proyecto le agregaremos una referencia de el ensamblado de nuestro proyecto Presentador
. Del lado del código de nuestro formulario, lo primero que debemos hacer es implementar de la interface IVista
, así:
public partial class Form1 : Form, IVistaOperaciones { public Form1() { InitializeComponent(); } public double Num1 { get { return !string.IsNullOrEmpty(textBox1.Text) ? Convert.ToDouble(textBox1.Text) : 0; } set { textBox1.Text = value.ToString(); } } public double Num2 { get { return !string.IsNullOrEmpty(textBox2.Text) ? Convert.ToDouble(textBox2.Text) : 0; } set { textBox2.Text = value.ToString(); } } public double Resultado { set { label1.Text = value.ToString(); } } }
Como se pueden dar cuenta, al implementar de la interface obligamos a esta clase (Form1.cs
) a implementar todos los miembros definidos en esta. La única lógica que debiera existir en esta clase es la de validaciones, como se ve en esta.
Ahora debemos crear un objeto de nuestro presentador, que será el único que conozca de la existencia del modelo y por tanto el único capaz de consumir sus métodos. Nuestra clase Form1.cs
nos quedará así:
public partial class Form1 : Form, IVistaOperaciones { private readonly PresentadorOperaciones _operaciones; public Form1() { InitializeComponent(); _operaciones = new PresentadorOperaciones(this); } #region Implementation of IVistaOperaciones public double Num1 { get { return !string.IsNullOrEmpty(textBox1.Text) ? Convert.ToDouble(textBox1.Text) : 0; } set { textBox1.Text = value.ToString(); } } public double Num2 { get { return !string.IsNullOrEmpty(textBox2.Text) ? Convert.ToDouble(textBox2.Text) : 0; } set { textBox2.Text = value.ToString(); } } public double Resultado { set { label1.Text = value.ToString(); } } #endregion private void Form1_Load(object sender, EventArgs e) { _operaciones.IniciarVista(); } private void button1_Click(object sender, EventArgs e) { _operaciones.ActualizarVista(); } }
Como podemos ver, la instancia del presentador tiene una relación directa con la tecnología de interfaz de usuario, porque realmente esta se entenderá con un contrato que no define más que el miembro a implementar.
Si ejecutamos vemos que después de todo esto, nuestra aplicación funciona, lo hemos logrado, pero dejar este post hasta aquí seria muy corto y no veríamos nada el potencial de MVP, que pasaría si tu cliente te dice «¡Ahora quiero sumar en internet! necesito ese proyecto en cinco minutos!!» pues bien, no es problema, simplemente agregamos un proyecto del tipo ASP.NET implementamos de nuestra Interface y listo!
En nuestro proyecto de asp.net en una pagina .aspx
creamos el mismo diseño que en el de windows forms, y del lado del código implementamos los miembros de la interface y creamos la instancia del presentador así:
public partial class _Default : System.Web.UI.Page, IVistaOperaciones { private PresentadorOperaciones _presentador; protected override void OnInit(EventArgs e) { base.OnInit(e); _presentador = new PresentadorOperaciones(this); Load += Page_Load; } protected void Page_Load(object sender, EventArgs e) { if(!IsPostBack) { _presentador.IniciarVista(); } } protected void btSumClick(object sender, EventArgs e) { _presentador.ActualizarVista(); } #region Implementation of IVistaOperaciones public double Num1 { get { return !string.IsNullOrEmpty(txtNum1.Text) ? Convert.ToDouble(txtNum1.Text) : 0; } set { txtNum1.Text = value.ToString(); } } public double Num2 { get { return !string.IsNullOrEmpty(txtNum2.Text) ? Convert.ToDouble(txtNum2.Text) : 0; } set { txtNum2.Text = value.ToString(); } } public double Resultado { set { lbResultado.Text = value.ToString(); } } #endregion }
Ese es realmente el potencial de MVP, la lógica de la aplicación le debe importar un pepino la tecnología de interfaz de usuario y la interfaz de usuario deberá llevar la menor lógica posible, así el mantenimiento de estas es más sencillo y el cambio de tecnología de interfaz de usuario es pan comido.
Espero les sea de utilidad.
Hasta el próximo post.
Quiser hacer una preguntaa mi dudad es esta:
Tengo un boton Ver Excel pero quiero que este boton me abra un archivo en excel 2007 que eh echo … Lo que he estado haciendo es este codigo:
// System.Diagnostics.Process.Start(Application.StartupPath + «/Excel»);
Donde Excel es mi carpeta donde recien se encuentra mi achivo excel.xlsx
Pero abre la carpeta donde esta el excel que eh creado mas no me abre el archivo directamente como podria solucionarlo Gracias…
Hola, antes que nada que pena por la tardanza al responder, he tenido mucho trabajo en estos dias.
Ahora si veamos tu problema. Estas bien, al hacer Process.Start(), lo que haces es iniciar un recurso de proceso y lo asocia a un componente Process (mas info aqui: http://msdn.microsoft.com/es-es/library/system.diagnostics.process.start.aspx). Como parametro pasas Application.StartupPath + «/Excel» siendo «Excel» una carpeta. Ahi esta tu error. Para asociar un recurso a un proceso (en este caso el Excel) no puedes pasar el nombre de un folder, debes pasar el nombre del archivo con SU EXTENCION. Es decir:
Application.StartupPath + «\Excel\file.xlsx» FIJATE EN LA BARRA INVERTIDA. De esta forma ya abres sin problema el archivo de Excel 😀
NOTA: Application.StartupPath Obtiene la ruta de acceso del archivo ejecutable que inició la aplicación, sin incluir el nombre del archivo ejecutable en este caso «\bin\Debug» no se si realmente quieras ubicar alli tus archivos de Excel.
Gracias por la pregunta, espero te sea de ayuda.
Tengo una preguntaa.. lo explicare lo mas sencillo posible:
Quiero imprimir defrente de un datatable ( en este datatable pondre mi store procedure ) y luego que cuando le de al boton imprimir me imprima desde la impresora prderteminada como podria serlo ,.. he visto por ahi printDocument.
Hola.
En el MSDN Magazine existe un excelente articulo con ejemplo incluido 😀
Te dejo la URL: http://msdn.microsoft.com/en-us/magazine/cc188767.aspx
Saludos.
Gracias hombre por fin entendí el rollo
Bacan, muchas gracias era lo que necesitaba
Gracias por tu explicación, me ha sido de gran utilidad, saludos desde Medellín Colombia
Hola Jose, gracias por comentar.
Es un gusto para mi que este articulo sea de tu útilidad.
Saludos para toda la comunidad de Medellin 😀
Excelente forma de explicar el MVP. Muchas gracias.
Excelente explicacion sobre el patron MVP, por fin lo tengo mas claro todo es rollo de ejemplo que baje.
GRACIAS!!!!!!
Compadre…. definitivamente gracias a personas como usted se nos facilita la vida perooo muchoooo
Muchas gracias, fue de gran ayuda.
Gracias por la explicación, ha facilitado al menos la compresión teórica del modelo.
Quisiera preguntarle de todas formas cierta duda:
– ¿En este modelo, es posible la utilización de AJAX y updatePanels?
Gracias
Claro que si, todos esos controles de WebForms hacen parte de la vista, no hay ningún problema en acomodarlos.
muchas gracias por la explicación amigo, y solo te quería pedir si tendrás un ejemplo? igual así de simple, pero con datos?
Muchas gracias por tu explicación, muy sencilla y clara, justo lo que necesitaba para entender el concepto. ¡Saludos!
Una pregunta, como regla de MVP debe existir un presentador por cada formulario ? o se puede usar un presentador para mas de un formulario?
Es aconsejable tener mas de un presentador por formulario?
Muchas Gracias.
Hola.
Q: «como regla de MVP debe existir un presentador por cada formulario o se puede usar un presentador para mas de un formulario?»
A: Como todo… depende, si se ajusta o no a tus necesidades y diseño. No hay reglas de oro que se deban aplicar si o si. Lo que pasaría es que se violaría el principio de unica responsabilidad si se implementa un diseño como ese.
Q: Es aconsejable tener mas de un presentador por formulario?
A: Va ligado a la primera pregunta. Habrían dos presentadores soportando el mismo IView y por qué? La vista estará soportando más de un View y por qué? Son cuestiones de diseño, que en principio pueden verse raras o erradas, pero que en definitiva dependerán de las necesidades a las que se deba dar solución.
Plantea bien como deberían ser representadas estas necesidades en la interfaz de usuario y construye de ahí hacía abajo… a ver si consigues un diseño con menos smells.
Puede ser usado un presentador para más de un formulario o solo debe existir uno para cada formulario?
Gracias.
Hola, creo que se respondió en el anterior comentario.
Gracias.
Hola pero el patrón MVP es un patrón de diseño que va implementado en la capa de presentacion por que creas varios proyectos?
Hola Pedro, gracias por tu comentario. Te ha pasado que al abrir un proyecto antiguo piensas: iughh, ¿yo programaba así? 😛 a veces pasa también con el código que dejas en el blog… 😛 Saludos
Hola amigo, era un comentario, mas bien si tuvieras tiempo de mejorarlo sería un buen aporte, pero si me ha servido de mucho tu blog, exitos!!!
Como haces cuando es una entidad que viene de otra capa, el IVista no se puede igualar con una entidad de dominio
Muy buena explicación. Gracias.