MVP es un patrón que muchos desarrolladores utilizan, aunque no lo sepan. ¿Quizá tú eres uno de ellos?
MVP (Model-View-Presenter) es un patrón de programación de interfaces gráficas. En él, la aplicación se divide en tres componentes:
- El model (modelo) trabaja con datos, realiza cálculos y dirige todos los procesos de negocio.
- La view (vista) muestra al usuario la interfaz y los datos del modelo.
- El presenter (presentador) sirve como interfaz entre el modelo y la vista.
Al igual que otros patrones similares (MVC, MVVM), MVP permite acelerar el desarrollo y dividir la responsabilidad de diferentes especialistas; la aplicación es más fácil de probar y mantener.
Se utiliza con más frecuencia en el desarrollo de aplicaciones móviles; sin embargo, es mucho más popular de lo que parece. Esto se debe a que este patrón se aplica activamente en la Web, aunque allí se llama MVC:
Cómo Funciona MVP
El diagrama anterior muestra que una aplicación creada según los principios de MVP funciona a través de la interfaz del modelo, la vista y el presentador. Funciona así:
- La vista construye la interfaz y agrega datos del modelo a ella.
- El usuario ve información e interactúa con la interfaz.
- La vista intercepta eventos y los transmite (delega) al presentador.
- El presentador procesa datos (no siempre) y los transfiere al modelo.
- El modelo realiza algunas operaciones y se actualiza (cambia ciertas propiedades).
- El presentador recibe el modelo actualizado y se lo transmite a la vista.
- La vista construye una interfaz con nuevos datos.
La principal diferencia entre MVP y MVC es que en MVC el modelo actualizado le dice a la vista misma que muestre otros datos. Si esto no sucede y la aplicación necesita un mediador como presentador, el patrón debe llamarse MVP.
Todo esto se puede comparar con el trabajo de una editorial:
- El autor prepara el texto (modelo).
- El texto lo recibe el editor (presentador).
- Si todo está bien con el texto, el editor lo transfiere al departamento de diseño (vista).
- Los diseñadores preparan un libro que comienzan a vender a los lectores (usuarios).
- Si los usuarios reaccionan de alguna manera al libro, como escribir cartas a la editorial, el trabajo puede comenzar de nuevo. Por ejemplo, alguien puede notar una inexactitud en el libro, entonces el editor transmitirá la información al autor, el autor la actualizará, etc.
Por supuesto, este no es un algoritmo exacto para el trabajo de una editorial, pero es suficiente para ilustrar los principios de MVP.
Ejemplo de Aplicación MVP
Como MVP (Modelo–Vista–Presentador) se utiliza para simplificar el desarrollo de interfaces gráficas, se puede considerar utilizando el ejemplo de una aplicación WPF. Como vista, actuarán los archivos MainWindow.xaml
(diseño de interfaz) y MainWindow.xaml.cs
(manejador de eventos).
Se puede comenzar con la creación de una interfaz de autorización:
Vista (MainWindow.xaml)
<Border HorizontalAlignment="Center" VerticalAlignment="Center" Width="250">
<StackPanel>
<TextBlock Text="Formulario de inicio de sesión" TextAlignment="Center"/>
<DockPanel LastChildFill="True">
<TextBlock Text="Inicio de sesión:"/>
<TextBox Name="LoginTextBox"/>
</DockPanel>
<DockPanel LastChildFill="True">
<TextBlock Text="Contraseña:"/>
<PasswordBox Name="PassBox"/>
</DockPanel>
<Button Content="Iniciar sesión" Name="LoginButton" Click="LoginButton_Click" IsDefault="True"/>
<TextBlock Name="MessageBlock"/>
</StackPanel>
</Border>
Ahora podemos pasar al archivo MainWindow.xaml.cs
Vista (MainWindow.xaml.cs)
using System.Windows;
using System.Windows.Controls;
public partial class MainWindow : Window
{
private Model model;
private Presenter presenter;
public MainWindow()
{
InitializeComponent();
model = new Model();
presenter = new Presenter(model);
}
private void LoginButton_Click(object sender, RoutedEventArgs e)
{
this.model = this.presenter.Login(LoginTextBox.Text, PassBox.Password);
Update();
}
private void Update()
{
MessageBlock.Text = this.model.Message;
}
}
Aquí ya se ha establecido la conexión necesaria entre los componentes. A continuación merece la pena considerar el código representativo:
Presentador
public class Presenter
{
private Model model;
public Presenter(Model model)
{
this.model = model;
}
public Model Login(string login, string password)
{
login = login.Trim();
password = password.Trim();
this.model.Login(login, password);
return this.model;
}
}
Recibe los datos, los procesa y los pasa al modelo. El modelo se actualiza y el representante lo devuelve a la vista.
Esto es lo que ocurre en el modelo:
Modelo
public class Model
{
private List<User> users;
private User loggedUser;
public Model()
{
users = new List<User>();
users.Add(new User("Name1","Login1","password1"));
users.Add(new User("Name2", "Login2", "password2"));
users.Add(new User("Name3", "Login3", "password3"));
users.Add(new User("Name4", "Login4", "password4"));
loggedUser = null;
}
public void Login(string login, string password)
{
bool hasLogged = false;
foreach (User user in this.users)
{
if (user.Login == login && user.Password == password)
{
this.loggedUser = user;
hasLogged = true;
break;
}
}
if (!hasLogged)
{
this.loggedUser = null;
}
}
public string Message
{
get
{
return this.loggedUser != null ? $"¡Me alegro de verte {this.loggedUser.Name}!" : "¡Nombre de usuario o contraseña incorrectos!";
}
}
}
Aquí es donde aparece una nueva entidad – Usuario. Ésta y otras clases similares se utilizan como datos con los que trabaja el modelo. Puede haber tantas clases como quieras: se manejan de la misma manera que fuera del patrón MVP. Aquí está la clase Usuario:
public class User
{
private string name;
private string login;
private string password;
public User(string name, string login, string password)
{
this.name = name;
this.login = login;
this.password = password;
}
public string Name
{
get
{
return this.name;
}
}
public string Login
{
get
{
return this.login;
}
}
public string Password
{
get
{
return this.password;
}
}
}
Conclusión
Quienes han leído nuestros otros artículos sobre patrones de diseño de interfaz gráfica de usuario habrán notado que en todos ellos la aplicación se divide en 3 componentes que se crean por separado. Este enfoque permite escribir más rápidamente partes individuales del programa y, al mismo tiempo, hacerlas más fáciles de entender.