Как выглядит структура mvc приложения

Обновлено: 19.09.2024

Copy raw contents

Архитектура MVC позволяет нам разделить код приложения на 3 части: Модель (Model), Вид или Представление (View) и Контроллер (Controller). Впервые она была описана в 1978 году, и предназначалась для приложений с графическим интерфейсом (окошками и кнопками), но позже была адаптирована и для веб-приложений.

Разделение на части позволяет упростить большой по объему код. Если код писать одним длинным скриптом, в нем становится тяжело разобраться, и тяжело вносить изменения, не допустив ошибку.

MVC не привязана к какому-то конкретному языку программирования, и не требует использования объектно-ориентированного программирования или какой-то другой парадигмы.

Разделение на части здесь не значит, что в коде должно быть ровно 3 файла (или 3 папки с файлами, или 3 класса) с названиями model, view и controller. MVC ничего не говорит нам по поводу того, как организовывать файлы с кодом. На практике модель часто занимает основной объем приложения, и представлена в виде большого числа разнотипных классов - сущностей, сервисов, классов работы с БД, и для каждого вида классов делают отдельные папки.

MVC применима к разным видам приложений - и к серверным веб-приложениям, и к десктопным (клиентским) приложениям. Разница между ними в том, что в веб-приложении программа получает один запрос от пользователя, обрабатывает его, выводит результат (обычно это веб-страница) и завершается. Если придет еще один запрос, будет запущена новая, независимая копия программы для его обработки. В отличие от веб-приложений, десктопные, мобильные приложения (а также написанные на яваскрипте приложения, которые работают на странице браузера) долгоживущие. Они обрабатывают много запросов от пользователя и обновляют информацию на экране, не завершаясь.

Компоненты MVC приложения

Модель содержит в себе всю логику приложения, она хранит и обрабатывает данные, при этом не взаимодействуя с пользователем напрямую (обратиться к Модели можно только из кода, вызывая ее функции). Например, сохранение информации в БД, проверка правильности введенных в форму данных — это задача Модели, но получение этих данных от пользователя или вывод информации на экран или обработка нажатия на кнопку — нет.

Например, в программе на PHP Модель не должна обращаться к внешним переменным вроде $_GET / $_POST / $_SESSION / $_COOKIE и не должна ничего выводить через echo . Все необходимые данные она получает через аргументы функций, и возвращает результат через return . А в программе на JS Модель не должна пытаться взаимодействовать с объектами вроде document и любыми DOM-элементами (DOM - это часть Представления).

Модель не должна никак зависеть и не должна ничего знать о Контроллерах и Видах.

Модель это не один класс или набор однотипных классов. Это основная часть приложения, которая может содержать много разных классов: сервисы, классы для взаимодействия с БД, сущности, валидаторы. В не-ООП приложении модель может просто представлять собой набор функций.

Представление отображает данные, которые ему передали. В веб-приложении оно обычно состоит из HTML-шаблонов страниц (урок про шаблоны), в десктопных или мобильных приложениях Преставление - это код, который отвечает за отображение информации на экране, отрисовку кнопочек и других элементов интерфейса.

В PHP оно не должно обращаться к внешним переменным ( $_GET и другие), его задача просто отобразить те данные, которые ему передали.

Может существовать несколько разных Представлений для вывода одних и тех же данных, например, в виде таблицы, графика или xls-файла.

Один Контроллер может работать с несколькими Моделями, и наоборот, одна Модель может использоваться в нескольких Контроллерах.

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

Взаимодействие компонентов MVC

Взаимодействие между компонентами MVC реализуется немного по-разному в серверных и в десктопных приложениях из-за того, что веб-приложение - короткоживущее, обрабатывает один запрос пользователя и завершается, а десктопное приложение обрабатывает много запросов без перезапуска.

В серверных приложениях обычно используется "пассивная" модель, а в десктопных приложениях - "активная". Активная модель, в отличие от пассивной, позволяет подписываться и получать уведомления об изменении в ней. В серверных приложениях это не требуется. Вот схема, изображающая взаимодействие компонентов:

Взаимодействие компонентов MVC

В схеме с активной моделью Вид подписывается на изменения в Модели. Затем, когда происходит какое-то событие (например, пользователь нажимает кнопку), вызывается Контроллер. Он дает Модели команду на изменение данных. Модель сообщает своим подписчикам (в том числе Виду), что данные изменились, и Вид обновляет интерфейс программы. Мы не будем далее разбирать этот вариант MVC, про него подробно написано в моем уроке по MVC в JS-приложениях.

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

Вооружившись этими знаниями, попробуем написать простейшее веб-приложение с использованием MVC.

Пример MVC приложения

Попробуем написать простейшее приложение - страницу с объявлениями. Обычно такие сайты хранят информацию в базе данных, но ради упрощения примера мы просто заложим данные прямо в Модели. При грамотном проектировании добавление работы с базой данных не потребует переделки Контроллера или Представления.

Вот дерево файлов, из которых состоит приложение:

Мы позже настроим веб-сервер так, чтобы корневой папкой была бы папка public , и из браузера нельзя было бы обратиться к файлам за пределами этой папки.

Начнем с Модели, так как она является по сути ядром приложения. Для того, чтобы представить Объявление в виде объекта, удобно использовать класс Post, а для того, чтобы хранить и управлять списком объявлений, мы сделаем сервис PostService. Для начала сделаем модель Объявления (которая будет являться частью Модели из MVC) и сохраним код в файл Post.php :

Теперь напишем сервис, который позволит нам получить список объявлений, добавлять или удалять их. Мы не будем усложнять код и добавлять постраничную выборку, сортировку, поиск, и т.д. Так как мы не используем базу данных, то изменения будут сохраняться только до завершения программы. Вот текст файла PostService.php :

Написав эти классы, мы получаем Модель, ядро нашего приложения. Используя ее, мы уже можем программно (не через интерфейс пользователя) работать со списком объявлений. Например, вот код, показывающий, как можно получить список объявлений, используя наш сервис:

Напишем также скрипт инициализации bootstrap.php , который будет инициализировать наше приложение, подключать нужные классы и создавать экземпляр сервиса.

Добавим к нашему приложению пользовательский интерфейс, который он сможет использовать в браузере. Для этого нам нужно добавить Контроллер и Вид.

Сначала напишем Контроллер, который будет при обращении к нему выводить список объявлений. Он будет запрашивать этот список у Модели и вызывать Вид, чтобы представить список в виде HTML страницы, которую отобразит браузер пользователя. Не будем использовать здесь функций или классов и напишем контроллер в виде простого скрипта public/list.php :

Осталось написать только Представление, которое будет отображать список объявлений в виде HTML страницы. Создадим файл view/list.phtml . Расширение phtml указывает, что это PHP-шаблон (урок про шаблонизацию):

Функция htmlspecialchars() нужна для корректного вывода спецсимволов вроде & или < в тексте или заголовке объявления и для предотвращения XSS уязвимости.

Протестируем написанное приложение. Для этого достаточно запустить встроенный в php веб-сервер, открыв командную строку, перейдя в папку public и набрав команду:

Другие классы, используемые в MVC приложениях

Антипаттерн: толстые контроллеры

Так как логика работы приложения заложена в модели, а контроллер лишь принимает запрос от пользователя и вызывает методы модели или представления, то контроллер получается небольшим по объему ("тонкий" контроллер и "толстая" модель). Однако неопытные разработчики часто думают, что модель отвечает только за взаимодействие с базой данных и пишут почти весь код обработки запроса в контроллере. Получается так называемый "уродливый толстый контроллер" и тем самым нарушается разделение модели и контроллера.

Идеи MVC сформулировал Трюгве Реенскауг (Trygve Reenskaug) во время работы в Xerox PARC в конце 70-х годов. В те времена для работы с ЭВМ было не обойтись без ученой степени и постоянного изучения объемной документации. Задача, которую Реенскауг решал совместно с группой очень сильных разработчиков, заключалась в том, чтобы упростить взаимодействие рядового пользователя с компьютером. Необходимо было создать средства, которые с одной стороны были бы предельно простыми и понятными, а с другой — давали бы возможность управлять компьютером и сложными приложениями. Реенскауг работал в команде, которая занималась разработкой портативного компьютера "для детей всех возрастов" — Dynabook, а также языка SmallTalk под руководством Алана Кея (Alan Kay). Именно тогда и там закладывались понятия дружелюбного интерфейса. Работа Реенскауга совместно с командой во многом повлияла на развитие сферы IT. Приведем интересный факт, который не относится к MVC напрямую, но иллюстрирует значимость тех разработок. В 2007 году после презентации Apple iPhone, Алан Кей сказал: “Когда вышел Macintosh, в Newsweek спросили, что я о нем думаю. Я сказал: это первый персональный компьютер, достойный критики. После презентации Стив Джобс подошел и спросил: достоин ли iPhone критики? И я ответил: сделайте его размером пять на восемь дюймов, и вы завоюете мир”. Спустя 3 года, 27 января 2010 года, Apple представила iPad диагональю 9,7 дюйма. То есть Стив Джобс почти буквально следовал совету Алана Кея. Проект, над которым работал Реннскауг велся на протяжении 10 лет. А первая публикация об MVC от его создателей вышла в свет еще через 10 лет. Мартин Фаулер, автор ряда книг и статей по архитектуре ПО, упоминает, что он изучал MVC по работающей версии SmallTalk. Поскольку информации об MVC из первоисточника долго не было, а также по ряду других причин, появилось большое количество различных трактовок этого понятия. В результате многие считают MVC схемой или паттерном проектирования. Реже MVC называют составным паттерном или комбинацией нескольких паттернов, работающих совместно для реализации сложных приложений. Но на самом деле, как было сказано ранее, MVC — это прежде всего набор архитектурных идей/принципов/подходов, которые можно реализовать различными способами с использованием различных шаблонов. Далее мы попробуем рассмотреть основные идеи, заложенные в концепции MVC.

Что такое MVC: основные идеи и принципы

  • VC — это набор архитектурных идей и принципов для построения сложных информационных систем с пользовательским интерфейсом;
  • MVC — это аббревиатура, которая расшифровывается так: Model-View-Controller.

Шаг 1. Отделить бизнес-логику приложения от пользовательского интерфейса

Часть 7. Знакомство с паттерном MVC (Model-View-Controller) - 3

Ключевая идея MVC состоит в том, что любое приложение с пользовательским интерфейсом в первом приближении можно разбить на 2 модуля: модуль, отвечающий за реализацию бизнес-логики приложения, и пользовательский интерфейс. В первом модуле будет реализован основной функционал приложения. Данный модуль будет ядром системы, в котором реализуется модель предметной области приложения. В концепции MVC данный модуль будет нашей буковкой M, т.е. моделью. Во втором модуле будет реализован весь пользовательский интерфейс, включая отображение данных пользователю и логику взаимодействия пользователя с приложением. Основная цель такого разделения — сделать так, чтобы ядро системы (Модель в терминологии MVC) могла независимо разрабатываться и тестироваться. Архитектура приложения после подобного разделения будет выглядеть следующим образом:

Шаг 2. Используя шаблон Наблюдатель, добиться еще большей независимости модели, а также синхронизации пользовательских интерфейсов

  1. Добиться еще большей независимости модели.
  2. Синхронизировать пользовательские интерфейсы.

Шаг 3. Разделение интерфейса на Вид и Контроллер

  • выводить и удобно отображать пользователю информацию о системе;
  • вводить данные и команды пользователя в систему (передавать их системе);
  1. Следуя принципам MVC, систему нужно разделять на модули.
  2. Самым важным и независимым модулем должна быть модель.
  3. Модель — ядро системы. Нужна возможность разрабатывать и тестировать ее независимо от интерфейса.
  4. Для этого на первом шаге сегрегации системы нужно разделить ее на модель и интерфейс.
  5. Далее, с помощью шаблона Наблюдатель, укрепляем модель в ее независимости и получаем синхронизацию пользовательских интерфейсов.
  6. Третьим шагом делим интерфейс на контроллер и вид.
  7. Все, что на ввод информации от пользователя в систему — это в контроллер.
  8. Все что на вывод информации от системы к пользователю — это в вид.

Немного о взаимосвязи Вида и Контроллера с Моделью

MVC: в чем профит?

Основная цель следования принципам MVC — отделить реализацию бизнес-логики приложения (модели) от ее визуализации (вида). Такое разделение повысит возможность повторного использования кода. Польза применения MVC наиболее наглядна в случаях, когда пользователю нужно предоставлять одни и те же данные в разных формах. Например, в виде таблицы, графика или диаграммы (используя различные виды). При этом, не затрагивая реализацию видов, можно изменить реакции на действия пользователя (нажатие мышью на кнопке, ввод данных). Если следовать принципам MVC, можно упростить написание программ, повысить читаемость кода, сделать легче расширение и поддержку системы в будущем. В завершающем материале цикла “Введение в Enterprise-разработку” мы с тобой посмотрим на реализацию MVC на примере Spring-MVC. Часть 8. Пишем небольшое приложение на spring-boot

Шаблон MVC

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

На следующей схеме показаны три основных компонента и существующие между ними связи.

Структура MVC

Такое распределение обязанностей позволяет масштабировать приложение в контексте сложности, так как проще писать код, выполнять отладку и тестирование компонента (модели, представления или контроллера) с одним заданием. Гораздо труднее обновлять, тестировать и отлаживать код, зависимости которого находятся в двух или трех этих областях. Например, логика пользовательского интерфейса, как правило, подвергается изменениям чаще, чем бизнес-логика. Если код представления и бизнес-логика объединены в один объект, содержащий бизнес-логику, объект необходимо изменять при каждом обновлении пользовательского интерфейса. Это часто приводит к возникновению ошибок и необходимости повторно тестировать бизнес-логику после каждого незначительного изменения пользовательского интерфейса.

Представление и контроллер зависят от модели. Однако сама модель не зависит ни от контроллера, ни от представления. Это является одним из ключевых преимуществ разделения. Такое разделение позволяет создавать и тестировать модели независимо от их визуального представления.

Функции модели

Модель в приложении MVC представляет состояние приложения и бизнес-логику или операций, которые должны в нем выполняться. Бизнес-логика должна быть включена в состав модели вместе с логикой реализации для сохранения состояния приложения. Как правило, строго типизированные представления используют типы ViewModel, предназначенные для хранения данных, отображаемых в этом представлении. Контроллер создает и заполняет эти экземпляры ViewModel из модели.

Функции представления

Функции контроллера

Контроллеры — это компоненты для управления взаимодействием с пользователем, работы с моделью и выбора представления для отображения. В приложении MVC представление служит только для отображения информации. Обработку введенных данных, формирование ответа и взаимодействие с пользователем обеспечивает контроллер. В структуре MVC контроллер является начальной отправной точкой и отвечает за выбор рабочих типов моделей и отображаемых представлений (именно этим объясняется его название — он контролирует, каким образом приложение отвечает на конкретный запрос).

Контроллеры не должны быть чересчур сложными из-за слишком большого количества обязанностей. Чтобы не перегружать логику контроллера, перенесите бизнес-логику из контроллера в модель предметной области.

Если ваш контроллер часто выполняет одни и те же виды действий, переместите эти действия в фильтры.

Маршрутизация

Маршрутизация на основе соглашений позволяет глобально определять форматы URL-адресов, которые принимает приложение, и то, как каждый из этих форматов соответствует конкретному методу действия на данном контроллере. При поступлении входящего запроса модуль маршрутизации выполняет синтаксический анализ URL-адреса и соотносит его с одним из определенных форматов URL-адресов, а затем вызывает метод действия связанного контроллера.

Маршрутизация атрибутов используется для указания сведений о маршрутизации путем добавления атрибутов, определяющих маршруты приложения, к контроллерам и действиям. Это означает, что определения маршрутов помещаются рядом с контроллером и действием, с которым они связаны.

Привязка модели

Проверка модели

Платформа обрабатывает проверку данных запроса на клиенте и на сервере. Логика проверки, указанная в типах модели, добавляется в готовые для просмотра представления в виде ненавязчивых заметок и реализуется в браузере с помощью подключаемого модуля jQuery Validation.

Внедрение зависимостей

Кроме того, приложение может использовать внедрение зависимостей в файлы представления с помощью директивы @inject :

Фильтры

Фильтры помогают разработчикам решать общие задачи, такие как обработка исключений или авторизация. Фильтры активируют пользовательскую логику предварительной и завершающей обработки для методов действий и могут быть настроены для запуска в определенные моменты в конвейерном выполнении определенного запроса. Фильтры могут применяться к контроллерам или действиям в виде атрибутов (или могут выполняться глобально). В состав платформы входит несколько фильтров (например, Authorize ). [Authorize] является атрибутом, который используется для создания фильтров авторизации MVC.

Области

Веб-API

Используйте функции создания ссылок для поддержки гипермедиа. Легко включайте поддержку общего доступа к ресурсам независимо от источника (CORS) для совместного использования веб-API в нескольких веб-приложениях.

Тестирование

Благодаря используемым интерфейсам и внедрению зависимостей платформа хорошо подходит для модульного тестирования. Кроме того, с помощью таких компонентов, как TestHost и поставщик InMemory для Entity Framework, можно быстро и просто выполнять интеграционные тесты. Узнайте больше о тестировании логики контроллеров.

Razor Просмотреть подсистему

С помощью Razor обработчика представлений можно определять макеты, частичные представления и заменяемые разделы.

Строго типизированные представления

Razor представления в MVC могут быть строго типизированы на основе модели. Контроллеры передают строго типизированную модель в представления для поддержки в них IntelliSense и проверки типов.

Например, следующее представление отображает модель типа IEnumerable<Product> :

Вспомогательные функции тегов

Вспомогательные функции тегов позволяют коду на стороне сервера принимать участие в создании и ОТРИСОВКЕ HTML-элементов в Razor файлах. Вспомогательные функции тегов используются для определения настраиваемых тегов (например, <environment> ) или для изменения поведения существующих тегов (например, <label> ). Вспомогательные функции тегов привязываются к определенным элементам на основе имени элемента и его атрибутов. Они предоставляют преимущества отрисовки на стороне сервера, сохраняя при этом возможности редактирования HTML.

С помощью EnvironmentTagHelper можно включать в приложения различные сценарии (например, необработанные или минифицированные) для конкретной среды выполнения (разработки, промежуточной или производственной):

Вспомогательные функции тегов обеспечивают удобный для HTML процесс разработки и расширенную среду IntelliSense для создания HTML и Razor разметки. Большинство встроенных вспомогательных функций тегов работают с существующими HTML-элементами и предоставляют для них атрибуты на стороне сервера.

Компоненты представлений

Компоненты представлений позволяют упаковывать логику отрисовки и повторно использовать ее в приложении. Они аналогичны частичным представлениям, но имеют связанную логику.

Не смотря на то, что схема MVC была первоначально разработана для персональных компьютеров, она была адаптирована и широко используется веб-разработчиками из-за точного разграничения задач и возможности повторного использования кода. Схема стимулирует развитие модульных систем, что позволяет разработчикам быстро обновлять, добавлять или удалять функционал.

В этой статье я опишу основные принципы, а также рассмотрю определение схемы построения и простой MVC PHP пример.

Что такое MVC

Название шаблона проектирования определяется тремя его основными составляющими частями: Модель, Представление и Контроллер. Визуальное представление шаблона MVC выглядит, как показано на приведенной ниже диаграмме:

Что такое MVC

На рисунке показана структура одностороннего потока данных и пути его следования между различными компонентами, а также их взаимодействие.

Модель

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

Представление

Представление также перехватывает действие пользователя, которое затем передается « Контроллеру ». Характерным примером этого является кнопка, генерируемая « Представлением ». Когда пользователь нажимает ее, запускается действие в «Контроллере».

Существует несколько распространенных заблуждений относительно компонента « Представление ». Например, многие ошибочно полагают, что « Представление » не имеет никакой связи с « Моделью », а все отображаемые данные передаются от « Контроллера ». В действительности такая схема потока данных не учитывает теорию, лежащую в основе MVC архитектуры. В своей статье Фабио Чеваско описывает этот некорректный подход на примере одного из нетрадиционных MVC PHP фреймворков:

«Чтобы правильно применять архитектуру MVC, между «Моделью» и «Представлением» не должно быть никакого взаимодействия: вся логика обрабатывается «Контроллером».

Кроме этого определение « Представления » как файла шаблона также является неточным. Но это не вина одного человека, а результат множества ошибок различных разработчиков, которые приводят общему заблуждению. После чего они неправильно объясняют это другим. На самом деле « Представление » это намного больше, чем просто шаблон. Но современные MVC -ориентированные фреймворки до такой степени впитали этот подход, что никто уже не заботится о том, поддерживается ли верная структура MVC или нет.

Контроллер

Его задача заключается в обработке данных, которые пользователь вводит и обновлении « Модели ». Это единственная часть схемы, для которой необходимо взаимодействие пользователя.

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

« Контроллер » получает задачи на выполнение только когда пользователь взаимодействует с « Представлением », и каждая функция зависит от взаимодействия пользователя с « Представлением ». Наиболее распространенная ошибка разработчиков заключается в том, что они путают « Контроллер » со шлюзом, поэтому присваивают ему функции и задачи, которые относятся к « Представлению ».

Также распространенной ошибкой является наделение « Контроллера » функциями, которые отвечают только за обработку и передачу данных из « Модели » в « Представление ». Но согласно структуре MVC паттерна это взаимодействие должно осуществляться между « Моделью » и « Представлением ».

MVC в PHP

Напишем на PHP веб-приложение, архитектура которого основана MVC . Давайте начнем с примера каркаса:

У нас есть проект с несколькими основными классами для каждой части шаблона. Теперь нужно настроить взаимосвязь между ними:

В приведенном выше примере PHP MVC нет никакого специфического функционала для контроллера, потому что в приложении не определены взаимодействия пользователя. Представление содержит весь функционал, так как наш пример предназначен лишь для демонстрации.

Давайте расширим пример, чтобы показать, как мы будем добавлять функционал контроллера, тем самым добавив в приложение взаимодействия:

Мы расширили программу базовым функционалом. Настройка взаимодействий между компонентами теперь выглядит следующим образом:

Запустите код и при нажатии на ссылку вы увидите строку для изменения данных.

Заключение

Мы рассмотрели базовую теорию модели MVC , и реализовали на его основе простое приложение. В следующей статье этой серии мы рассмотрим несколько конкретных случаев, с которыми вы можете столкнуться при создании веб-приложений на PHP .

Читайте также: