MVP – Model View Presenter – паттерн организации PL (presentation layer – уровень представления).
MVP применяется при создании десктопных интерфейсов. Выделяют три комопнента: есть модель – группа классов, которые отдают данные или получают команды, представление – форма обладающая состоянием и некоторым поведением. Презентер создают для отделения бизнес-логики от деталей GUI-фреймворка. В отличие от MVC в MVP представление определяет презентер, а не наоборот.
MVP обычно строится вокруг существующих GUI-фреймворков. На практике существуют две принципиально различные различные реализации паттерна – Supervising Controller и Passive View.
В первом случае логика помещается в обработчики событий button_click, а сами обработчики помещаются в отдельный класс. Для полной изоляции презентера от деталей представления надо писать достаточно много врапперов\адаптеров.
Во втором случае создается пара интерфейсов для общения между представлением и презентером. При совершении какого-либо действия представление напрямую обращается к презентеру, тот выполняет некоторый код и вызывает установку свойств представления. Passive View способствует максимальному перемещению кода в в презентер, что облегчает тестирование.
Создание MVP в WinForms с помощью Unity.
Создаем новое winforms приложение и грохаем оттуда форму.
Для начала определим служебные интерфейсы.
//Маркерный интерфейс представления
public interface IView
{
}
//Интрефейс презентера
public interface IPresenter<T> where T:IView
{
T View { get; set; }
}
//Базовый класс для презентера
public abstract class BasePresenter<T> :IPresenter<T> where T:IView
{
public T View { get; set; }
}
Класс “бизнес-логики” будем использовать тот же, что и в предыдущем посте.
public interface ISayHelloService
{
string SayHello(string name);
}
public class SayHelloSerivce : ISayHelloService
{
public string SayHello(string name)
{
return "Привет, " + name;
}
}
Теперь определим рабочие интерфейсы.
public interface ISayHelloPresenter : IPresenter<ISayHelloView>
{
void SayHello();
}
public interface ISayHelloView: IView
{
string GetInputText();
void SetOutputText(string text);
}
Для перезнтера код будет тривиальный.
public class SayHelloPresenter : BasePresenter<ISayHelloView>, ISayHelloPresenter
{
ISayHelloService _service;
public SayHelloPresenter(ISayHelloService service)
{
this._service = service;
}
public void SayHello()
{
this.View.SetOutputText(_service.SayHello(this.View.GetInputText()));
}
}
Теперь нарисуем простую формочку:
В коде напишем следеющее:
public partial class Form1 : Form, ISayHelloView
{
ISayHelloPresenter _presenter;
public Form1(ISayHelloPresenter presenter)
{
InitializeComponent();
_presenter = presenter;
//Циклическая зависимость
_presenter.View = this;
}
public string GetInputText()
{
return textBox1.Text;
}
public void SetOutputText(string text)
{
textBox2.Text = text;
}
private void button1_Click(object sender, EventArgs e)
{
_presenter.SayHello();
}
}
Циклическая зависимость в Passive View не позволяет с помощью контейнера пропихнуть все зависимости. Поэтому передача презентеру ссылки на представление делается в коде view.
Теперь чтобы увязать это вместе надо создать и сконфигурировать контейнер. Сделаем это прямо в Program.cs.
static void Main()
{
var container = new UnityContainer();
container
.RegisterType<ISayHelloService, SayHelloSerivce>()
.RegisterType<ISayHelloPresenter, SayHelloPresenter>()
.RegisterType<ISayHelloView, Form1>()
;
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run((Form)container.Resolve<ISayHelloView>());
}
Вот и все.
