MVP: Patrón Supervising Controller para WinForms

En mi entrada anterior explique el patrón Passive View". Ahora es el turno del patrón Supervising Controller.

Este patrón aprovecha la infraestructura de binding que posee winforms. A diferencia del Passive View, el View en este caso conoce un modelo al cual conectarse. Dado que el view puede interactuar con un modelo tanto su implementación como su interfaz es bastante mas simple en comparación con Passive View.

El Presenter en este caso se ocupa de la lógica mas complejas, mientras que el view se ocupa de la lógica simple, delegando esto último al mecanismo de databinding que usualmente se define de forma declarativa.

Sin más preámbulo, y siguiendo con nuestro ejemplo anterior esta es la interfaz del view:

public interface IEditarClienteView
{
    Cliente ClienteEnEdicion { get; set; }
    void CargarPaises(IEnumerable<Pais> paises);

    event EventHandler GuardarClick;
    event EventHandler CancelarClick;
    void Show();
    void Close();
}

Estos son los tests de nuestro presenter:

[TestFixture]
public class EditarClientePresenterTests
{
    public IRepositorioPaises CrearDobleDeRepositorioPaises()
    {
        var repositorioPaises = new Mock<IRepositorioPaises>();
        repositorioPaises.Setup(r => r.ObtenerOrdenadosPorNombre())
            .Returns(new[]
                         {
                             new Pais {CodigoIso = "AR", Nombre = "Argentina"},
                             new Pais {CodigoIso = "UY", Nombre = "Uruguay"}
                         });

        return repositorioPaises.Object;
    }

    private static IRepositorioClientes CrearDobleRepositorioClientes()
    {
        var repositorio = new Mock<IRepositorioClientes>();
        var cliente = new Cliente
        {
            Nombre = "Jose",
            Apellido = "Romaniello",
            Pais = new Pais { CodigoIso = "AR", Nombre = "Argentina" }
        };
        repositorio.Setup(r => r.Obtener(1)).Returns(cliente);
        return repositorio.Object;
    }

    [Test]
    public void AlIniciarCargarPaises()
    {
        var view = new Mock<IEditarClienteView>();

        var repositorioPaises = CrearDobleDeRepositorioPaises();
        var presenter = new EditarClientePresenter(
            view.Object,
            CrearDobleRepositorioClientes(),
            repositorioPaises);

        presenter.Iniciar(1);

        view.Verify(v => v.CargarPaises(repositorioPaises.ObtenerOrdenadosPorNombre()));
    }

    [Test]
    public void CuandoInicioCargoLosDatosDelCliente()
    {
        var view = new Mock<IEditarClienteView>();
        view.SetupAllProperties();

        var repositorioClientes = CrearDobleRepositorioClientes();

        var presenter = new EditarClientePresenter(
            view.Object,
            repositorioClientes,
            CrearDobleDeRepositorioPaises());

        presenter.Iniciar(1);
        view.Object.ClienteEnEdicion
            .Should().Be.SameInstanceAs(repositorioClientes.Obtener(1));
    }


    [Test]
    public void CuandoInicioMostrarForm()
    {
        var view = new Mock<IEditarClienteView>();
        view.SetupAllProperties();

        var presenter = new EditarClientePresenter(
            view.Object,
            CrearDobleRepositorioClientes(),
            CrearDobleDeRepositorioPaises());

        presenter.Iniciar(1);

        view.Verify(v => v.Show());
    }

    [Test]
    public void CuandoPresionoElBotonGuardarEntoncesGuardoConRepositorio()
    {
        var view = new Mock<IEditarClienteView>();
        view.SetupAllProperties();

        var repositorioClientes = CrearDobleRepositorioClientes();
        var cliente = repositorioClientes.Obtener(1);

        var presenter = new EditarClientePresenter(
            view.Object,
            repositorioClientes,
            CrearDobleDeRepositorioPaises());

        presenter.Iniciar(1);

        view.Raise(v => v.GuardarClick += null, EventArgs.Empty);

        Mock.Get(repositorioClientes).Verify(r => r.Actualizar(cliente));
    }


    [Test]
    public void CuandoCanceloCierroElForm()
    {
        var view = new Mock<IEditarClienteView>();
        view.SetupAllProperties();

        new EditarClientePresenter(
            view.Object,
            CrearDobleRepositorioClientes(),
            CrearDobleDeRepositorioPaises());

        view.Raise(v => v.CancelarClick += null, EventArgs.Empty);

        view.Verify(v => v.Close());
    }


}

Esta es la implementación del presenter:

public class EditarClientePresenter
{
    private readonly IEditarClienteView editarClienteView;
    private readonly IRepositorioClientes repositorioClientes;
    private readonly IRepositorioPaises repositorioPaises;

    public EditarClientePresenter(
        IEditarClienteView editarClienteView,  
        IRepositorioClientes repositorioClientes, 
        IRepositorioPaises repositorioPaises)
    {
        this.editarClienteView = editarClienteView;
        this.repositorioClientes = repositorioClientes;
        this.repositorioPaises = repositorioPaises;
        editarClienteView.GuardarClick += EditarClienteViewGuardarClick;
        editarClienteView.CancelarClick += EditarClienteViewCancelarClick;
    }

    void EditarClienteViewCancelarClick(object sender, EventArgs e)
    {
        editarClienteView.Close();
    }

    void EditarClienteViewGuardarClick(object sender, EventArgs e)
    {
        repositorioClientes.Actualizar(editarClienteView.ClienteEnEdicion);
        editarClienteView.Close();
    }

    public void Iniciar(int idCliente)
    {
        editarClienteView.CargarPaises(repositorioPaises.ObtenerOrdenadosPorNombre());
        editarClienteView.ClienteEnEdicion = repositorioClientes.Obtener(1);
        editarClienteView.Show();
    }
}

Ahora bien, esta es la implementación de nuestro view:

public partial class FormEditarCliente : Form, IEditarClienteView
{
    public FormEditarCliente()
    {
        InitializeComponent();
    }

    public Cliente ClienteEnEdicion
    {
        get { return (Cliente) clienteBindingSource.DataSource; }
        set { clienteBindingSource.DataSource = value; }
    }

    public void CargarPaises(IEnumerable<Pais> paises)
    {
        paisBindingSource.DataSource = paises;
    }

    public event EventHandler GuardarClick
    {
        add { GuardarButton.Click += value; }
        remove { GuardarButton.Click -= value; }
    }

    public event EventHandler CancelarClick
    {
        add { CancelarButton.Click += value; }
        remove { CancelarButton.Click -= value; }
    }
}

"clienteBindingSource" y "paisBindingSource" son controles en nuestro formulario creados al hacer databinding. Si alguien no conoce como hacer binding en winforms, aquí les dejo un mini-screencast:

Unable to display content. Adobe Flash is required.

Por último el código fuente puede ser descargado desde esta enlace.


blog comments powered by Disqus
  • Categories

  • Archives