пятница, 9 ноября 2012 г.

Architecture / Model-View-Presenter (MVP) Theory (RU)

Как-то совершенно незаметно привычная модель MVC в моих проектах мутировала к MVP (Model View Presenter). Возможно тому способствовали прочтения Мартина Фаулера и использование разных фреймворков вроде SwiftSuspenders или Parsley.

Далее в тексте буду стараться использовать именно английские названия Model, View, Controller, Presenter, чтобы избежать ненужных коннотаций с их русскими аналогами.

MVC vs MVP


Не взирая на все плюсы MVC, у него есть и большие проблемы. Одна из них:
читабельность кода и отладка — дело в том, что настраивать систему, построенную на событиях (Observer Synchronization), задача достаточно не тривиальная и без trace и breakpoint часто сложно реализуемая.

Другая, связана с тем, что в MVC нет места промежуточной логике и состояниям, у Мартина Фаулера есть хороший пример, сводящийся к вопросу:
«если у нас есть таблица данных, а так же визуальное отображение ее, а так же мы хотим менять цвет текста в зависимости от значения в ячейке. Куда нам следует поместить код логики расцветки? Какой слой должен отвечать за включение кнопки сохранения, после изменения данных во View?»

Из последней проблемы Мартин и выводит необходимость промежуточного слоя - Presenter.

Что же такое MVP?


MVP => Model-View-Presenter
View - это визуальные компоненты (виджеты), из которых состоит приложение. Их поведение описывается в Presenter, он фактически представляет из себя Controller MVC, с раздутыми правами, который, может манипулировать как Model так и View. Model - данные предметной области и бизнес логика.

Интересная дискуссия на (stackoverflow), куда же положить бизнес-логику (Bussiness logic) контроллер или модель. Кратко, - если бизнес логику положить в контроллер, то ее сложно будет использовать повторно в других контроллерах.

Основное внимание дальше уделим взаимодействию View и Presenter. Но стоит кратко заметить, что создатель MVP - Mike Potel, в своем труде рекомендует изменения Model осуществлять при помощи системы команд (которые являются частью Model), т.к. шаблон команда (Command Pattern) облегчает реализацию undo, redo взаимодействия.


Почему же нельзя выбросить промежуточный слой абстракции – Presenter, и производить инъекции Model непосредственно во View?

Посмотрим чем же занимается Presenter, одна из ролей его, это описание взаимодействия с пользователем и их последствия, которые не должны находиться в Model, но с другой стороны избавлены тонкостей View. От чего мы можем подставив вместо View - Mock-объект и протестировать Presenter без использования реального пользовательского интерфейса. Идея берет начало от так называемых «Скромных Объектов» (Humble Object) описаных Майклом Фезером в статье "Скромное диалоговое окно" (Michael Feathers, The Humble Dialog Box).

Еще один аргумент, на который так мы программисты любим напирать, но который почти никогда не случается — это то, что мы сможем подменить максимально тонкий View, другим. Говорят, что это работает с .NET, когда мы можем подменить Windows Form, Silverlight-ом, но с ними я не имел дело, поэтому ручаться не могу. Другой распространенный пример — Unix, где с приложением можно работать, как через GUI, так и через командную строку.

Три формы Presenter


В итоге, написание взаимодействия View-Presenter сводиться к 3-м шаблонам, описанных Мартином Фаулером:

Passive View - Presenter берет на себя всю синхронизацию данных между View и Model. View не хранит указателей на Model.

под Contoller-ом автор подразумевает Presenter

Supervising Presenter - Presenter подписывается на события View и забирает эти данные, после чего меняет Model, на изменения которой подписан View (т.е. View может содержать ссылку на Model). Для более сложной логики (цвет, выбранный элемент и другое, Presenter может вмешиваться устанавливая их вручную во View.

под Contoller-ом автор подразумевает Presenter

Presentation Model - может на себя брать роль синхронизации данных (как Passive View) так и отдавать синхронизацию на View (как Supervising Presenter). Однако, в дополнение ко всему, Presentation Model хранит изменяемое состояние пользователя, которое не относиться к Model, к примеру выбранный элемента, либо включенная (Enabled) кнопка.

Еще интересно замечание Мартина на счет Presentation Model.
Если вам необходимо покрыть тестами логику взаимодействия с пользователем, но при этом не задеть виджеты (View), то Presentation Model должен ссылаться на View через интерфейс, за которым можно при тестировании скрыть Stub-объект. В таком случае PM должен заниматься синхронизацией данных между View и PM.

В любом случае, выбор формы связывания View и Presenter должен лежать в плоскости простоты, и наглядности (к примеру использование Data Bindings), а так же необходимой степени покрытия тестами.

PS

Схемы взяты из замечательной статьи Derek Greer - Interactive Application Architecture Patterns.
Фото из моего instagram-а.

Link


  1. Michael Feathers, The Humble Dialog Box ("Скромное диалоговое окно").
  2. Мартин Фаулер об архитектуре приложенией.
  3. github - SwiftSuspenders.
  4. github - Parsley.
  5. Запись вебинара "Паттерн MVC как архитектурная концепция. Место ли ему в Вашей архитектуре?" (Спасибо Мише за ссылку)

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

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

Press Any Key...