Organización y almacenaje

Patrón MVP: cómo funciona el Modelo-Vista-Presentador y cómo implementarlo

10 min de lectura

Patrón MVP: cómo funciona el Modelo-Vista-Presentador y cómo implementarlo

El patrón Modelo-Vista-Presentador (MVP) es una arquitectura utilizada para organizar aplicaciones con interfaz gráfica separando la lógica de presentación de la propia interfaz y de los datos de la aplicación. Su objetivo principal es evitar que una única clase termine acumulando la gestión de eventos, la lógica de negocio y la representación visual.

MVP pertenece a la familia de patrones de arquitectura para interfaces de usuario relacionados con MVC y otras variantes de separación entre presentación y lógica. Martin Fowler explica que existen distintas interpretaciones de MVP y que el patrón evolucionó hacia variantes como Supervising Controller y Passive View.

La idea fundamental es sencilla: la Vista muestra información y recibe las acciones del usuario, el Presentador coordina lo que debe suceder y el Modelo representa los datos y la lógica de la aplicación.

¿Qué es el patrón MVP?

MVP son las siglas de Model-View-Presenter, que en español podemos traducir como Modelo-Vista-Presentador.

La arquitectura divide la aplicación en tres elementos:

┌──────────────┐
│    VISTA     │
│ Interfaz     │
└──────┬───────┘
       │
       │ acciones
       ↓
┌──────────────┐
│ PRESENTADOR  │
│ Lógica de    │
│ presentación │
└──────┬───────┘
       │
       ↓
┌──────────────┐
│    MODELO    │
│ Datos y      │
│ lógica       │
└──────────────┘

La separación permite colocar cada responsabilidad en un lugar concreto. La Vista no debería encargarse de resolver toda la lógica de la aplicación, mientras que el Modelo no debería depender de los elementos visuales.

Esta separación entre presentación y lógica de dominio es uno de los principios fundamentales de las arquitecturas de interfaces.

¿Qué hace el Modelo?

El Modelo representa los datos y las operaciones que necesita la aplicación.

Por ejemplo, imaginemos una aplicación para gestionar usuarios. El Modelo podría encargarse de operaciones como:

obtenerUsuario()
crearUsuario()
actualizarUsuario()
eliminarUsuario()

El Modelo podría comunicarse con una base de datos, una API o cualquier otra fuente de información.

Una posible clase sería:

public class Usuario {
    private String nombre;
    private String email;

    public Usuario(String nombre, String email) {
        this.nombre = nombre;
        this.email = email;
    }

    public String getNombre() {
        return nombre;
    }

    public String getEmail() {
        return email;
    }
}

En una aplicación real, el Modelo puede estar formado por varias clases, repositorios, servicios y otras estructuras.

Lo importante es que no debería contener código específico de la interfaz gráfica.

¿Qué hace la Vista?

La Vista es la parte encargada de mostrar información al usuario y recoger sus acciones.

Puede ser una ventana, una página, una pantalla de una aplicación móvil o cualquier otro componente de interfaz.

Por ejemplo:

Nombre: Antonio
Email: antonio@example.com

[ Guardar ]

Cuando el usuario pulsa el botón Guardar, la Vista comunica esa acción al Presentador.

En una implementación de tipo MVP, la Vista intenta mantenerse sencilla. En algunas variantes, puede limitarse prácticamente a mostrar los datos que recibe del Presentador. En otras, puede encargarse de pequeñas operaciones de presentación.

Martin Fowler distingue precisamente diferentes variantes de MVP según cuánto trabajo de presentación queda en la Vista y cuánto asume el Presentador.

¿Qué hace el Presentador?

El Presentador es el componente que coordina la interacción entre la Vista y el Modelo.

Por ejemplo, cuando el usuario pulsa un botón:

Usuario
   ↓
Vista
   ↓
Presentador
   ↓
Modelo
   ↓
Presentador
   ↓
Vista

El Presentador puede recibir la acción, solicitar información al Modelo y decidir qué debe mostrar la Vista.

Por ejemplo:

public class UsuarioPresenter {

    private UsuarioView view;

    public UsuarioPresenter(UsuarioView view) {
        this.view = view;
    }

    public void cargarUsuario() {
        Usuario usuario = obtenerUsuario();

        view.mostrarNombre(usuario.getNombre());
        view.mostrarEmail(usuario.getEmail());
    }

    private Usuario obtenerUsuario() {
        return new Usuario(
            "Antonio",
            "antonio@example.com"
        );
    }
}

La Vista no necesita conocer todos los detalles de cómo se obtiene el usuario.

¿Cómo funciona MVP?

Podemos entender el funcionamiento mediante un ejemplo sencillo.

Imaginemos una pantalla con un botón:

┌─────────────────────────┐
│                         │
│      Cargar usuario     │
│                         │
│       [ CARGAR ]        │
│                         │
└─────────────────────────┘

El usuario pulsa el botón.

La Vista informa al Presentador:

Vista
  │
  │ usuarioPulsoCargar()
  ↓
Presentador

El Presentador solicita los datos:

Presentador
  │
  │ obtenerUsuario()
  ↓
Modelo

El Modelo devuelve la información:

Modelo
  │
  │ Usuario
  ↓
Presentador

Finalmente, el Presentador indica a la Vista qué debe mostrar:

Presentador
  │
  │ mostrarUsuario()
  ↓
Vista

El resultado podría ser:

Nombre: Antonio
Email: antonio@example.com

Implementación sencilla de MVP

Una forma sencilla de implementar MVP consiste en crear una interfaz para la Vista.

Por ejemplo:

public interface UsuarioView {

    void mostrarNombre(String nombre);

    void mostrarEmail(String email);

    void mostrarError(String mensaje);
}

Después podemos crear el Presentador:

public class UsuarioPresenter {

    private UsuarioView view;

    public UsuarioPresenter(UsuarioView view) {
        this.view = view;
    }

    public void cargarUsuario() {

        try {

            Usuario usuario = obtenerUsuario();

            view.mostrarNombre(usuario.getNombre());
            view.mostrarEmail(usuario.getEmail());

        } catch (Exception e) {

            view.mostrarError(
                "No se ha podido cargar el usuario"
            );
        }
    }

    private Usuario obtenerUsuario() {

        return new Usuario(
            "Antonio",
            "antonio@example.com"
        );
    }
}

La implementación concreta de la Vista dependerá del entorno utilizado.

Podría ser una actividad Android, una ventana de escritorio, una página web o cualquier otro tipo de interfaz.

Ejemplo completo

Podemos representar una pequeña aplicación de usuarios mediante tres componentes.

Modelo

public class Usuario {

    private String nombre;

    public Usuario(String nombre) {
        this.nombre = nombre;
    }

    public String getNombre() {
        return nombre;
    }
}

Vista

public interface UsuarioView {

    void mostrarUsuario(String nombre);

    void mostrarError(String mensaje);
}

Presentador

public class UsuarioPresenter {

    private UsuarioView view;

    public UsuarioPresenter(UsuarioView view) {
        this.view = view;
    }

    public void cargarUsuario() {

        Usuario usuario =
            new Usuario("Antonio");

        view.mostrarUsuario(
            usuario.getNombre()
        );
    }
}

La comunicación quedaría así:

             Usuario
                │
                ↓
        ┌───────────────┐
        │    MODELO     │
        └───────┬───────┘
                │
                ↓
        ┌───────────────┐
        │  PRESENTADOR  │
        └───────┬───────┘
                │
                ↓
        ┌───────────────┐
        │     VISTA     │
        └───────────────┘

MVP y separación de responsabilidades

Una de las principales razones para utilizar MVP es mantener separadas las responsabilidades.

Sin una arquitectura de este tipo, podríamos terminar con una clase que haga simultáneamente:

Leer botón
Consultar base de datos
Validar información
Realizar cálculos
Actualizar interfaz
Gestionar errores
Guardar datos

A medida que la aplicación crece, este enfoque puede dificultar el mantenimiento y las pruebas.

Con MVP podemos distribuir esas responsabilidades:

Vista
→ Interfaz y eventos

Presentador
→ Lógica de presentación

Modelo
→ Datos y operaciones

La separación no implica necesariamente que cada aplicación tenga exactamente las mismas clases o relaciones. MVP ha tenido diferentes implementaciones y variantes a lo largo del tiempo.

MVP y las pruebas unitarias

Una de las razones por las que MVP resulta interesante es que permite trasladar buena parte de la lógica de presentación fuera de la interfaz gráfica.

Esto facilita probar el Presentador de forma independiente.

Por ejemplo, podemos crear una Vista simulada:

public class UsuarioViewFake
        implements UsuarioView {

    public String nombre;

    @Override
    public void mostrarUsuario(String nombre) {
        this.nombre = nombre;
    }

    @Override
    public void mostrarError(String mensaje) {
    }
}

Después podemos comprobar el comportamiento del Presentador:

UsuarioViewFake view =
    new UsuarioViewFake();

UsuarioPresenter presenter =
    new UsuarioPresenter(view);

presenter.cargarUsuario();

Y comprobar que:

view.nombre

contiene el resultado esperado.

La idea de separar la lógica de presentación facilita este tipo de pruebas porque no es necesario ejecutar toda la interfaz para comprobar determinados comportamientos. Fowler señala precisamente la capacidad de probar la lógica sin depender de la interfaz como una de las ventajas de separar la presentación.

MVP frente a MVC

MVP y MVC (Model-View-Controller) comparten muchas ideas, por lo que es habitual confundirlos.

En términos generales:

MVP MVC
Modelo Modelo
Vista Vista
Presentador Controlador
El Presentador coordina la interacción El Controlador gestiona determinadas acciones
Puede utilizar una Vista pasiva La relación entre Vista y Controlador depende de la implementación

Sin embargo, no existe una única implementación universal de MVC ni de MVP. La terminología ha evolucionado y distintos frameworks utilizan estos nombres de maneras diferentes.

Por eso es más útil fijarse en qué responsabilidades tiene realmente cada componente que simplemente en el nombre de la arquitectura.

MVP frente a MVVM

Otra arquitectura relacionada es MVVM (Model-View-ViewModel).

MVVM introduce un ViewModel que representa el estado y comportamiento relacionado con la presentación, normalmente utilizando mecanismos de enlace de datos para sincronizar la interfaz con ese estado. Microsoft, por ejemplo, describe MVVM como una forma de desacoplar el código de interfaz del código que no pertenece a la interfaz.

La diferencia conceptual puede resumirse así:

MVP

Vista
  ↕
Presentador
  ↕
Modelo

Mientras que en MVVM:

Vista
  ↕
ViewModel
  ↕
Modelo

Aunque los conceptos pueden parecer similares, las responsabilidades concretas dependen de la implementación y del framework utilizado.

Variantes de MVP

No existe una única forma de implementar MVP.

Entre las variantes históricas se encuentra Passive View, donde la Vista es deliberadamente pasiva y el Presentador asume prácticamente toda la lógica de presentación. Fowler también describe Supervising Controller, donde la Vista puede encargarse de parte de la lógica sencilla mientras el Presentador gestiona las interacciones y la lógica más compleja.

Esto explica por qué dos proyectos que utilizan MVP pueden tener estructuras bastante diferentes.

Ventajas del patrón MVP

MVP puede aportar varias ventajas cuando se utiliza en aplicaciones con una interfaz suficientemente compleja.

Separación de responsabilidades

Cada componente tiene una función más concreta, lo que facilita comprender la estructura del programa.

Mayor facilidad para realizar pruebas

La lógica del Presentador puede probarse sin tener que depender completamente de la interfaz gráfica.

Mantenimiento

Separar la lógica de presentación de los elementos visuales puede facilitar los cambios posteriores.

Reutilización

Una misma lógica de presentación puede utilizarse con diferentes implementaciones de una Vista en determinados diseños.

Menos lógica en la interfaz

La Vista puede mantenerse más sencilla al delegar las decisiones de presentación en el Presentador.

Inconvenientes de MVP

MVP también tiene algunos inconvenientes.

El principal es que añade más estructura y más clases al proyecto.

Una aplicación pequeña que solamente tiene una pantalla sencilla podría necesitar muy poco código adicional para funcionar correctamente. Introducir Modelo, Vista, Presentador e interfaces puede ser innecesario si el problema es muy pequeño.

Además, un Presentador puede terminar creciendo demasiado si se le asignan demasiadas responsabilidades.

Por ejemplo:

UsuarioPresenter
 ├── usuarios
 ├── pagos
 ├── pedidos
 ├── autenticación
 ├── navegación
 ├── estadísticas
 └── configuración

En ese caso, la separación inicial de responsabilidades empieza a perder sentido.

La separación de presentación también implica cierta maquinaria adicional y puede aumentar la complejidad del proyecto. Fowler señala precisamente este equilibrio entre una mejor separación y el coste de introducir más estructura.

¿Cuándo utilizar MVP?

MVP puede resultar apropiado cuando tenemos una aplicación con una interfaz suficientemente compleja y queremos mantener separada la lógica de presentación.

Puede ser especialmente interesante cuando:

  • Hay bastante lógica asociada a las pantallas.
  • Se necesitan pruebas unitarias de esa lógica.
  • La interfaz cambia con frecuencia.
  • Existen diferentes implementaciones de una misma Vista.
  • Se quiere evitar que las clases de interfaz crezcan demasiado.
  • El proyecto tiene una estructura suficientemente grande como para justificar la separación.

En cambio, para una aplicación muy pequeña, introducir una arquitectura completa puede aportar más complejidad que beneficios.

En resumen

El patrón Modelo-Vista-Presentador (MVP) divide una aplicación en tres responsabilidades principales:

MODELO
Datos y operaciones

VISTA
Interfaz y eventos

PRESENTADOR
Coordina la interacción

La Vista recibe las acciones del usuario y las comunica al Presentador. Este utiliza el Modelo para obtener o modificar información y posteriormente indica a la Vista qué debe mostrar.

El flujo básico es:

Usuario
   ↓
Vista
   ↓
Presentador
   ↓
Modelo
   ↓
Presentador
   ↓
Vista

Su principal objetivo es separar la lógica de presentación de la interfaz y del resto de la aplicación. Esta separación puede mejorar la organización y facilitar las pruebas, aunque también introduce más clases y complejidad.

MVP no es una receta única: existen diferentes variantes y formas de distribuir las responsabilidades. Por eso, al implementarlo conviene adaptar la arquitectura al framework utilizado y al tamaño real del proyecto, evitando añadir capas innecesarias.

En definitiva, MVP resulta especialmente útil cuando una interfaz empieza a acumular demasiada lógica y necesitamos una forma clara de separar lo que el usuario ve, lo que ocurre cuando interactúa con la aplicación y los datos que utiliza el programa.

Productos recomendados

Seleccionados de nuestra tienda para este tema

Plafón LED de techo rectangular ultrafino de 23 pulgadas… Hasta -75% Plafón LED de techo rectangular ultrafino de 23 pulgadas… El precio original era: 263,92 €.El precio actual es: 65,98 €. Ver opciones
Si eres de jerseys navideños feos, este de El Regreso al… -95% Si eres de jerseys navideños feos, este de El Regreso al… El precio original era: 226,50 €.El precio actual es: 11,99 €. Añadir al carrito
Leave a comment

Carrito de compra

×

No hay productos en el carrito.