6 ноября 2008 г.

Паттерн MVP для ASP.NET WebForms

Сейчас разрабатываю проект на ASP.NET. Для отделения представления данных в слое отображения от логики их формирования и обработки применяю паттерн Model-View-Presenter.

Суть паттерна в том, чтобы ASP.NET форма (View) реализовывала интерфейс, с которым сможет работать представление данных (Presenter), которое в свою очередь оперирует объектами предметной области (Model).

Таким образом, логика приложения отделяется от представления данных. Также, появляется возможность протестировать эту логику.

Теперь от теории к практике. Здесь есть один важный вопрос. Надо ли нам, "пробрасывать" события из Представления в Контроллер?

Первый вариант без "проброса":

   1:  public partial class CreateNewUser : Page, ICreateNewUserView
   2:  {
   3:      protected override void OnInit(EventArgs e)
   4:      {
   5:          base.OnInit(e);
   6:          Submit.Click += Submit_Click;
   7:      }
   8:   
   9:      private void Submit_Click(object sender, EventArgs e)
  10:      {
  11:          if (!Page.IsValid)
  12:              return;
  13:   
  14:          Presenter.SubmitCreation();
  15:          Response.Redirect("RegistrationInformation.aspx");
  16:      }
  17:   
  18:      // остальная часть опущена
  19:  }
  20:   
  21:  public class CreateNewUserPresenter
  22:  {
  23:      private void SubmitCreation()
  24:      {
  25:          if (string.IsNullOrEmpty(view.Email))
  26:              return;
  27:   
  28:          // логика создания пользователя
  29:      }
  30:  }

За первый вариант:

Контроллеру нет разницы после какого события произошел вызов. Если завтра это должно происходить по нажатию на ImageButton или при каком-то другом событии, мы не будем менять Контроллер и интерфейс ICreateNewUserView.

Код Контроллера на много легче тестируется.

Интерфейс Представления содержит только необходимые данные для работы Контроллера. К примеру, Контроллер может запросить поле Email из Представления (и не имеет значения содержится это поле в TextBox или рисуется на экране).

Как следствие - проще сделать mock-объект для интерфейса ICreateNewUserView.

Против первого варианта:

Определённая часть бизнес правил остается в Представлении. К примеру, вызов SubmitCreation только после проверки страницы на валидность.

Второй вариант с "пробросом":

   1:  public partial class CreateNewUser : Page, ICreateNewUserView
   2:  {
   3:      protected override void OnInit(EventArgs e)
   4:      {
   5:          base.OnInit(e);
   6:          Presenter.Init();
   7:      }
   8:  }
   9:   
  10:  public class CreateNewUserPresenter
  11:  {
  12:      public void Init()
  13:      {
  14:          view.Submit.Click += Submit_Click;
  15:      }
  16:   
  17:      private void Submit_Click(object sender, EventArgs e)
  18:      {
  19:          if (!view.Page.IsValid)
  20:              return;
  21:   
  22:          SubmitCreation();
  23:          HttpContext.Current.Response.Redirect("RegistrationInformation.aspx");
  24:      }
  25:  }
  26:      // остальная часть опущена
  27:  }
  28:   
  29:  public class CreateNewUserPresenter
  30:  {
  31:      private void SubmitCreation()
  32:      {
  33:          if (string.IsNullOrEmpty(view.Email))
  34:              return;
  35:   
  36:          // логика создания пользователя
  37:      }
  38:  }

За второй вариант:

Возможность протестировать практически всю работу по представлению данных.

Против второго варианта:

Сильная связность Представления и Контроллера. Контроллер должен знать практически все об элементах, из которых построено Представление, и их типах.

Как следствие - "увеличение" интерфейса Представления.

Затруднённое тестирование и создание mock-объектов для Представлений.

Сначала я пробовал второй вариант. Мне очень понравилась идея протестировать буквально все в классе CreateNewUser. Но, к сожалению, поддержка такого варианта работы занимает очень много времени. Поэтому сейчас я остановился на первом варианте. "Пассивное представление" очень удобно в реализации и достаточно хорошо тестируется.

Возможно я упустил какие-то важные моменты и все-таки стоит перейти на второй вариант?

Обсуждение темы на GotDotNet.ru

Ссылки

IoC-контейнер в ASP.NET с использованием шаблона MVP (Model-View-Presenter)

GUI Architectures

Model-View-Presenter: Variations on the Basic Pattern

Комментариев нет:

Отправить комментарий