Шаблон разработки ПО — Model View Controller (MVC)

MVC - один из самых распространённых архитектурных шаблонов разработки. Часто используется в различных фреймворках. В том числе и в Django.

ПрограммыWebОбразованиеDjango

Model-View-Controller, или же "Модель-Представление-Контроллер" - это паттерн (шаблон), рекомендующий разделять приложение на три части (в некотором смысле - также "трёхзвенная архитектура"). В сущности, приложение рекомендуется разделить на:

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

В своё время, как и многие замечательные открытия 70-х / 80-х годов (тот же графический интерфейс, который позже появился в Apple Lisa) MVC был придуман в компании Xerox. И долгое время данный паттерн был "одним из", но при развитии веб-фреймворков, получил вторую жизнь. В частности, используется в самых популярных веб-фреймворках языков PHP, Python, Ruby - Symfony / Laravel, Django, Ruby on Rails.

Суть паттерна Model-View-Controller

Каждую хранимую логическую сущность мы можем представить в виде модели. При чём в каждой конкретной модели мы можем реализовать методы, предоставляющие удобный доступ к данным. Также можно реализовать базовую модель, от которой прочие будут наследоваться для определения общих методов, предоставляющих общий интерфейс для всех моделей - всё же для почти всех моделей будет логично иметь базовые функции: Create, Read, Update, Delete (CRUD) - создание, чтение, обновление и удаление объектов данной модели.

Часто данные - самая важная часть приложения. Поэтому будет полезно изолировать данные и методы взаимодействия с ними. Более того, определив интерфейс для работы с моделью, будет полезно его придерживаться, дабы реже менять модель. Вместо этого стоит определить интерфейсы для взаимодействия с моделью. И подстраивать уже контроллер под модель.

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

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

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

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

В другую же сторону слой представления отдаёт данные пользователю. И в плане преобразования вида данных из внешних во внутренние и наоборот он не сильно отличается от процесса приёма данных - здесь уже будут кодеры в целевой формат, упаковщики данных, генераторы форм, HTML-шаблонизаторы и т.д. Также здесь могут стоять модули для сжимающего кодирования, оптимизации изображений/видео, кеши ответов.

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

Представьте, что наше приложение изначально было написано для настольных компьютеров с ОС Windows. Позднее мы решили, что выгоднее сделать web-версию. Если наш код был написан монолитом, то весь код придётся переписывать. Если же мы отделили представление данных от логики работы с данными и хранения данных, то можно будет переписать лишь ту часть, что отвечала за представление, то есть интерфейс для пользователя.

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

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

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

Толстые модели и простые контроллеры

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

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

Почти во всех ситуациях лучше держать контроллеры максимально простыми. Есть даже анти-паттерн: Толстые Тупые Уродливые Контроллеры (ТТУК). Попытка уместить логику в контроллер приводит к тому, что контроллеры, отвечающие за разные пользовательские действия будут содержать логику для работы с разными моделями в одном месте. При таком подходе модели и контроллеры слишком сильно связываются, теряется модульность.

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

Фото Что есть мотивация? Зачем мотивация тебе?

Что есть мотивация? Зачем мотивация тебе?

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