Pull to refresh

Шпаргалка по MV-паттернам для проектирования веб-приложений

Reading time 7 min
Views 67K
mv-patterns
В Интернет можно найти множество различающихся реализаций и схем, уже набившего оскомину, паттерна MVC. В разных книгах я также встречал разные схемы. Это порождает некоторую путаницу и комментарии к моей предыдущей статье: "Реализация MVC паттерна на примере создания сайта-визитки на PHP" тому подтверждение. В поисках истины, я попытался расставить все по местам… перечитал некоторую литературу и статьи по паттернам проектирования и написал дополнение к упомянутой статье. Но решил запостить это дополнение, как отдельный топик в надежде на фидбэк. Под катом вы найдете несколько часто встречающихся схем MVC и MVP с описанием жизненного цикла приложения, а также описание менее популярных паттернов HMVC и MVVM. Разумеется, некоторые из перечисленных паттернов применимы не только к веб-приложениям, но в статье они рассматриваются именно в этом контексте.


MVC


Шаблон MVC (Модель-Вид-Контроллер или Модель-Состояние-Поведение) описывает простой способ построения структуры приложения, целью которого является отделение бизнес-логики от пользовательского интерфейса. В результате, приложение легче масштабируется, тестируется, сопровождается и конечно же реализуется.

Некоторые умозаключения об MVC
Модель — содержит бизнес-логику приложения и включает методы выборки (это могут быть методы ORM), обработки (например, правила валидации) и предоставления конкретных данных, что зачастую делает ее очень толстой, что вполне нормально.
Модель не должна напрямую взаимодействовать с пользователем. Все переменные, относящиеся к запросу пользователя должны обрабатываться в контроллере.
Модель не должна генерировать HTML или другой код отображения, который может изменяться в зависимости от нужд пользователя. Такой код должен обрабатываться в видах.
Одна и та же модель, например: модель аутентификации пользователей может использоваться как в пользовательской, так и в административной части приложения. В таком случае можно вынести общий код в отдельный класс и наследоваться от него, определяя в наследниках специфичные для подприложений методы.
Вид — используется для задания внешнего отображения данных, полученных из контроллера и модели.
Виды cодержат HTML-разметку и небольшие вставки PHP-кода для обхода, форматирования и отображения данных.
Не должны напрямую обращаться к базе данных. Этим должны заниматься модели.
Не должны работать с данными, полученными из запроса пользователя. Эту задачу должен выполнять контроллер.
Может напрямую обращаться к свойствам и методам контроллера или моделей, для получения готовых к выводу данных.
Виды обычно разделяют на общий шаблон, содержащий разметку, общую для всех страниц (например, шапку и подвал) и части шаблона, которые используют для отображения данных выводимых из модели или отображения форм ввода данных.
Контроллер — связующее звено, соединяющее модели, виды и другие компоненты в рабочее приложение. Контроллер отвечает за обработку запросов пользователя. Контроллер не должен содержать SQL-запросов. Их лучше держать в моделях. Контроллер не должен содержать HTML и другой разметки. Её стоит выносить в виды.
В хорошо спроектированном MVC-приложении контроллеры обычно очень тонкие и содержат только несколько десятков строк кода. Чего, не скажешь о Stupid Fat Controllers (SFC) в CMS Joomla. Логика контроллера довольно типична и большая ее часть выносится в базовые классы.
Модели, наоборот, очень толстые и содержат большую часть кода, связанную с обработкой данных, т.к. структура данных и бизнес-логика, содержащаяся в них, обычно довольно специфична для конкретного приложения.

По запросу «MVC» в интернете можно найти множество различных схем, в которых очень легко запутаться. Попробуем расставить все по местам. Рассмотрим схему 1:
mvc1

1. При заходе пользователя на веб-ресурс, скрипт инициализации создает экземпляр приложения и запускает его на выполнение.
2. Выполняется действие index фронт-контроллера, которое генерирует представление главной страницы.
3. Представление отображается пользователю.

Первые три шага — это простая цепочка, без использования модели. Далее идет последовательность, где задействована модель:

4. После того, как приложение получит запрос от пользователя, создается экземпляр запрошенного контроллера и вызывается указанное действие.
5. В этом действии вызываются методы модели, изменяющие ее.
6. Генерируется представление (или же представление оповещается об обновлении модели).
7. Представление запрашивает данные для отображения.
8. Модель возвращает запрошенные данные.
9. Представление отображает результаты пользователю.


Встречается и такая схема — схема 2:
mvc2

1. Контроллера получает следующий запрос от пользователя.
2. Далее в зависимости от внутренней логики:
      2a. Формируется представление какой-то страницы.
      2b. Либо, вызываются методы модели.
3. Модель уведомляет представление об изменениях.
4. Представление обновляется (если в цепочке была задействована модель) и отображается пользователю.


На некоторых схемах можно увидеть стрелку от представления к контроллеру. Рассмотрим этот случай — схема 3:
view-controller
  1. Приложение получает еще один запрос от пользователя: создает экземпляр запрашиваемого контроллера и вызывает указанное действие.
  2. В действии генерируется представление содержащее некоторую форму ввода данных.
  3. Представление с формой отображается пользователю.
  4. После того как пользователь заполнит форму и нажмет на кнопку «Submit» вызывается тот же контроллер, который проверяет и обрабатывает полученные из формы данные и формирует другое представление или же обновляет текущее.

Шаг 4, по сути, эквивалентен еще одному шагу 1, инициализирующему новый цикл… Поэтому на всех схемах можно предполагать неявную связь в направлении от вида к контроллеру.


MVP


А теперь посмотрим на схему паттерна MVP (Model-View-Presenter) — схема 4:
mvp
  1. После того, как приложение получит запрос от пользователя, определяется запрошенный Presenter и действие. Приложение создает экземпляр этого Presenter'а и запускает метод действия.
  2. В методе действия могут содержаться вызовы модели, к примеру, считывающие информацию из базы данных.
  3. Модель возвращает данные.
  4. После этого действие формирует представление, в которое передаются данные полученные из модели.
  5. Сформированное представление отображается пользователю.

Помимо MVP существуют и другие, производные от MVC, паттерны, например MVVM и HMVC.


HMVC


Реализация паттерна HMVC (Hierarchical Model View Controller — Иерарархические Модель-Контроллер-Вид) используется в веб-фреймворке Kohana. Рассмотрим в чем же ключевое отличие от MVC — схема 5:

hmvc
Приложение представляет иерархию независимых друг от друга MVC триад. При этом, каждая триада может напрямую обратиться к контроллеру другой триады. Такой подход позволяет решить некоторые проблемы масштабируемости приложений, имеющих классическую MVC-архитектуру, уменьшить зависимость между различными частями приложения, облегчить дальнейшую поддержку и повторное использование кода. Более подробно о преимуществах данного подхода можете прочитать в статье "Масштабирование веб-приложений с помощью HMVC".


MVVM


MVC прижился в веб-приложениях во многом потому, что он отлично справляется со сценарием запрос-ответ.
Основная черта такого сценария — короткое время жизни View. Приходит запрос, который передается на соответствующий контроллер, он инициирует какие-то процессы в модели, а затем создается View, который просто заполняется данными из модели и передается клиенту в браузер. Все в один проход.

Когда появился Ajax и богатые клиент-сайд приложения (RIA), оказалось, что MVC не очень хорошо подходит для работы с областями страницы или приложения, что привело к несколько иным моделям: MVP (Model View Presenter) и затем к MVVM (Model View ViewModel). Если c паттернами MVC и MVP большинство более-менее знакомо, то о последнем слышали очень немногие.

Первоначально MVVM был описан для Silverlight и имеет преимущества для сложных интерфейсов с определенной логикой, которая отличается от логики приложения. MVVM отличается более «тесной» связью между Моделью и Представлением посредством слоя Представление-Модель, который синхронизирует данные как при событии на стороне Модели, так и на стороне Представления.

В MVC логика зашита в Модели, ее можно также помещать в Контроллер, но это справедливо подвергается критике (Stupid Fat Controller). В MVVM, напротив, логика помещается в «промежуточный» слой ViewModel. Рассмотрим схему MVVM — схема 6:
mvvm
Вы определяете видимую область экрана и задаете самые общие данные о нем, не зная, какое содержание будет показываться во время выполнения. Для HTML схема MVVM особо удачна благодаря DOM, который, как известно, вполне может вмещать данные. Поэтому MVVM была успешна реализована во фреймворке KnockOut.JS. Изначально все просто. Есть Модель данных, предоставленная сервером. Есть Представление в виде DOM, в виде разметки. А есть Представление-Модель (Вид Модели, если хотите), которая описывает изменение Представления, связывает Модель и Представление, причем синхронизует эту связь.

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


Заключение


Не нужно пытаться ответить на вопрос, какой из упомянутых паттернов лучше и т.п… нужно вынести только главную идею — «разделяй и властвуй», потому как очень важно чтобы слои были максимально независимыми и легко заменяемыми.

Подборка полезных ссылок

Полезные ссылки


Чуть более подробнее о MVVM, MVP и MVC применительно к клиент-сайд приложениям…

Другие паттерны:

IoC & DI

АОП

Подборка в этом спойлере не совсем по сабжу… я сделал ее скорее для себя, т.к. в ходе написании статьи веб-серфинг остановился именно на этих ссылках.
Tags:
Hubs:
+63
Comments 22
Comments Comments 22

Articles