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

    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

    АОП

    Подборка в этом спойлере не совсем по сабжу… я сделал ее скорее для себя, т.к. в ходе написании статьи веб-серфинг остановился именно на этих ссылках.
    Share post

    Similar posts

    Comments 22

      +5
      Не хватает решаемых проблем и получаемого результата. Этим паттерны должны учить приёмам декомпозиции задачи и композиции её решения, а не тупо быть шаблоном.
        +1
        MVC слишком абстрактный паттерн, чтобы к нему можно было сделать примеры. Я думаю, это даже не паттерн, а концепция. Не зря существует столько вариаций. Это не какой нибудь Strategy.
          +1
          надо давать примеры работающих приложений, иллюстрирующие те или иные концепции.
          с DOM удачно получилось, а остальное без примерно малопонятно для тех, кто на практике не сталкивался с чем-то подобным. то есть получается, что статья написана для тех, кто уже это знает, и бесполезна для тех, кто не знает.
            0
            без примерно *без примеров*
            +1
            Он же все равно решает какую-то проблему? И результат всегда оказывается с компромиссами, какими? Хотелось бы это увидеть в статье.
              0
              MB решают проблему «разделяй и влавствуй»? =) Более конкретной проблемы для таких паттернов у меня фантазии не хватает придумать с ходу.
                0
                Юные разработчики, вооружившись этой статьёй, тоже будут придумывать проблемы для оправдания использования паттерна :)
                  +1
                  Думается, если разработчик хоть и юный, но имеющий голову на плечах, он таки попробует копнуть глубже в тему и посмотреть примеры фреймворков хотя бы построенных на этих паттернах. А если головы на плечах нет… думаю пояснять не надо. Итак капитанствую.
                    0
                    Вы правы, понимайте меня также
                      0
                      Понимаю, и согласен с вами =)
              +1
              Эта парадигма, и она точно сформулирована:
              www.itu.dk/courses/VOP/E2005/VOP2005E/8_mvc_krasner_and_pope.pdf

              При этом в wiki достаточно точные переданы терминология и идеи данной парадигмы.
                +2
                Вы не устали нести свет в наши неосведомленные умы? За ссылку спасибо :)
                  0
                  Честно говоря, за прошедшие дни MVC на Хабре — заколебался :)
                  Думаю это последний топик по этой теме, где за ближайшие пол-года отписался.
                  Гори они все пламенем, да по что синем, да что б эффектно -))
                    0
                    heim.ifi.uio.no/~trygver/themes/mvc/mvc-index.html тут описана сама идея MVC.
                      0
                      Лучше мою ссылку посмотреть.
                      в 79 году он только начинал развивать идею.
                      Когда именно сформировали парадигму.
            +1
            по AMD вот хорошая презентация Закаса — http://www.slideshare.net/nzakas/scalable-javascript-application-architecture
            Не лишним будет добавить ее к статье.
              0
              Последний паттерн это не AMD.
              The Asynchronous Module Definition (AMD) API specifies a mechanism for defining modules such that the module and its dependencies can be asynchronously loaded.
              AMD — это спосбо организации модулей, а не приложения.

              Последний пункт не имеет устоявшегося названия, Николас Закас(автор этого подхода) его называет Scaleable JavaScript Application Architecture (SJSAA).
              Есть несколько реализаций этого подхода. Одна из них и статья описывающая подробности реализации.

              PS Ядро приложения развивается за счет Расширений (не хватает облачка).
                0
                Так черным по белому написано, что это — «спецификация описывающая модульные приложения»… которая была создана, как альтернатива CJS. Информация отсюда; и эта ссылка также приведена в тексте. А вот, дополнительная ссылка на презентацию и на github.
                  0
                  В вашей статье написано, что
                  AMD (Asynchronous Module Definition) — спецификация, описывающая модульные приложения, в которых элементы приложения оформлены блоками интерфейса и реализованы каждый в своем модуле. Эти модули друг с другом не обмениваются. Они знают только о песочнице, в которой происходит их выполнение. Песочница, в свою очередь, имеет дело с ядром приложения, которое запускает процесс и следит за последовательностью действий.

                  Так вот это не AMD.

                  В статье на которую вы опирались есть фраза «Для модульных приложений создана спецификация описания модулей AMD (и альтернативная CJS).» она не говорит, что следующая за ней схема это AMD.
                    0
                    отписал в личку.
                0
                Немного подкорректировал статью, учитывая рекомендации более опытных коллег, в частности убрал раздел с упоминанием AMD (Asynchronous Module Definition) — спецификация описывающая построение модульных приложений, способ организации модулей в клиент-сайд приложении. Взаимодействие между модулями же может происходить по MV* схеме.

                Схема, в которых элементы приложения оформлены блоками интерфейса и реализованы каждый в своем модуле, выполняемыми в песочнице — Scaleable JavaScript Application Architecture. При этом модули непосредственно друг с другом не обмениваются. И она так же плохо подходит под понятие MV*, т.к. не описывает ни модель ни представление, а описывает иную иерархию приложения. При этом, модули могут быть реализованы на схемах MVC в частности.

                Вот как-то так… с этим еще нужно будет как следует разобраться. Для тех кому не терпится, еще несколько ссылок:

                Еще раз спасибо хабраюзеру azproduction.

              Only users with full accounts can post comments. Log in, please.