Охота на мифический MVC. Обзор, возвращение к первоисточникам и про то как анализировать и выводить шаблоны самому

— Не понимаю, почему люди так восхищаются этим Карузо? Косноязычен, гугнив, поёт — ничего не разберешь!
— А вы слышали, как поёт Карузо?
— Да, мне тут кое-что из его репертуара Рабинович напел по телефону.

Детектив по материалам IT. Часть первая


Я осознаю, что писать очередную статью на тему Модель-Вид-Контроллер это глупо и вредно для «кармы». Однако с этим «паттерном» у меня слишком личные отношения — проваленный проект, полгода жизни и тяжелой работы «в корзину».


Проект мы переписали, уже без MVC, просто руководствуясь принципами — код перестал быть похож на клубок спагетти и сократился наполовину (об этом позже, в обещанной статье про то, как мы применяли «принципы» в своем проекте). Но хотелось понять, что же мы сделали не так, в чем была ошибка? И в течении долгого времени изучалось все, что содержало аббревиатуру MVC. До тех пор пока не встретились исходные работы от создателя — Трюгве Реенскауга…


И тогда все встало на свои места. Оказалось что фактически на основе принципов мы пере-изобретали «original MVC». А то, что зачастую преподносится как MVC, не имеет к нему никакого отношения… впрочем также как и к хорошей архитектуре. И судя по тому сколько людей пишет о несостоятельности «классического MVC», спорит о нем и изобретает его всевозможные модификации, не одни мы столкнулись с этой проблемой.


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



Мифы: MVC создавался для языка SmallTalk

Концепция MVC была сформулирована Трюгве Реенскаугом (Trygve Reenskaug) в результате его работы в Xerox PARC в 1978/79 годах. Как правило создание MVC связывают с языком SmallTalk, но это не совсем так. На самом деле Реенскауг работал в группе, занимавшейся разработкой портативного компьютера «для детей всех возрастов» Dynabook под руководством Алана Кэя (Alan Kay).


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


Именно тогда/там закладывались основы графического интерфейса, формировалось понятие «дружелюбного интерфейса». А также разрабатывался язык SmallTalk, вместе с концепциями объектно-ориентированного программирования, чтобы неподготовленный пользователь «мог понимать и писать программы». Вот как описывает увиденное в Xerox PARC в 1979 году Стив Джобс — How Steve Jobs got the ideas of GUI from XEROX (from 6.30)


Проект велся около 10 лет, группой очень сильных разработчиков. Найденные в результате решения, подходы, принципы и в области пользовательских интерфейсов, и в области объектно ориентированного программирования и вообще в разработке больших и сложных компьютерных систем были в какой-то степени проссумированы Реенскаугом и составили основу MVC. Так что MVC это действительно прежде всего совокупность направляющих архитектурных идей. В SmallTalk-80 эти идеи всего лишь получили свою первую значимую реализацию. Причем сделано это было уже после ухода Реенскауга из Xerox PARC и без его участия.


К сожалению в течении долго времени о «реальном MVC» не было практически никакой доступной информации. Первая серьезная публикация от создателей появилась лишь 10 лет спустя — «A Cookbook for Using the Model-View-Controller User Interface Paradigm in Smalltalk-80». Даже Фаулер упоминает, что он изучал MVC по работающей версии SmallTalk — «у меня был доступ к работающей версии Smalltalk-80, чтобы я мог изучить MVC. Я не могу сказать, что это исследование было тщательным, но оно позволило мне понять некоторые аспекты решения, которые другие описания объяснить не смогли».


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


В результате многие действительно считают MVC схемой или паттерном (из-за чего постоянно возникает вопрос — какая же из множества существующих схем «правильная» и почему их так много?). В более продвинутом варианте MVC называют составным паттерном, то есть комбинацией нескольких паттернов, работающих совместно для реализации сложных приложений (тут обычно упоминаются Observer, Strategy и Composite). И лишь немногие понимают, что MVC это прежде всего набор архитектурных идей/принципов/подходов, которые могут быть реализованы различными способами с использованием различных шаблонов…


К последним относится в частности Мартин Фаулер. Вот что он пишет: «MVC часто называют паттерном, но я не вижу особой пользы воспринимать его как паттерн, поскольку он включает в себя множество различных идей. Разные люди читают про MVC в различных источниках и извлекают от туда разные идеи, но называют их одинаково — «MVC». Это приводит к большой путанице и кроме того служит источником недоразумений и непониманию MVC, как будто бы люди узнавали про него через «испорченный телефон»…. Я уже потерял счет сколько раз я видел что-то, описываемое как MVC, которое им не оказывалось.»[ GUI Architectures]


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


Но сначала ссылки. Исходный доклад Реенскауга — «The original MVC reports». Позже Реенскауг все это более четко сформулировал и оформил в своей последующей работе «The Model-View-Controller (MVC). Its Past and Present». Возможно кому-то будет интересна страница, где собраны записи Ренскауга, относящиеся к тому периоду, с его комментариями — MVC XEROX PARC 1978–79.


Уже упоминавшаяся первая публикация о MVC в языке SmallTalk-80 от разработчиков только в улучшенном качестве «A Description of the Model-View-Controller User Interface Paradigm in the Smalltalk-80 System» (Glenn Krasner и Stephen Pope). Хорошим дополнением служит также статья «Applications Programming in Smalltalk-80.How to use Model-View-Controller» (автор SteveBurbeck учавствовал в разработке компилятора SmallTalk для IBM на основе Smalltalk-80, а также в разработке MacApp). Ну и если кто-то хочет полного погружения — «Smalltalk-80. The Interactive Programming Environment» от знаменитой Адель Голдберг в дискуссиях с которой Реенскаугом и создавались термины Model, View, Controller.


Схемы MVC


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


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


  1. Модель ничего не знает ни о Виде, ни о Контроллере, что делает возможным ее разработку и тестирование как независимого компонента. И это является главным моментом MVC.


  2. Вид отображает Модель. И значит, он каким-то образом должен получать из нее нужные для отображения данные. Наиболее распространены следующие два варианта: 1) Активный Вид, который знает о Модели и сам берет из нее нужные данные. 2) Пассивный Вид, которому данные поставляет Контроллер. В этом случае Вид с Моделью никак не связан.


    Видов может быть несколько — они могут по разному отображать одни и те же данные, например в виде таблицы или графика, или же отвечать за отображение разных частей данных из Модели.


  3. Контроллер является пожалуй самым неоднозначным компонентом. Тем не менее общим является то, что Контроллер всегда знает о Модели и может ее изменять (как правило в результате действий пользователя).


    А также он может осуществлять управление Видом/Видами (особенно если их несколько) и соответственно знать о Видах, но это не обязательно.



Отсюда мы получаем базовые (максимально упрощенные) схемы двух наиболее часто встречающихся разновидностей MVC. Перечеркнутой линией обозначена необязательная связь Контроллера с Видом.


Model View Controller


Вот так базовая схема выглядит у Фаулера: «Основные связи между Моделью, Видом и Контроллером. (Я называю их основными, потому что на самом деле Вид и Контроллер могут быть связанными друг с другом непосредственно. Однако, разработчики в основном не используют эту связь.)»:


Model View Controller


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


Но обычно, под MVC все таки подразумевают вариант с Активной Моделью.


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


Шаблон Наблюдатель позволяет Модели с одной стороны информировать Вид или Контроллер о том что в ней произошли изменения, а с другой фактически ничего о них «не знать» (кроме того что они реализуют некий заданный интерфейс «подписчика») и тем самым оставаться независимой. Это называется слабым связыванием и считается вторым ключевым моментом MVC.


Именно поэтому, когда говорится, что MVC это составной шаблон, то в первую очередь в качестве одного из его компонентов упоминается паттерн Наблюдатель. На диаграммах слабое связывание принято рисовать пунктирной стрелкой, но многие это правило игнорируют.


Таким образом, более продвинутые «схемы MVC» будут выглядеть так:


Model View Controller


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


Ну и последнее. Вообще говоря MVC, в любой своей разновидности, это прежде всего шаблон для разработки приложений с пользовательским интерфейсом и его главное назначение — обеспечить взаимодействие приложения с пользователем. Поэтому в полноценной MVC схеме (явно или неявно) должен присутствовать пользователь. И тут в основном встречаются две трактовки:


  1. Пользователь управляет приложением через Контроллер, а Вид служит исключительно для отображения информации о Модели, и пользователь его лишь видит


    Model View Controller


    Часто указывают/рисуют лишь то, что пользователь действует на Контроллер, а то что он видит Вид опускается.


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


    Model View Controller



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


Для иллюстрации всего вышесказанного несколько диаграмм «из интернета» (надеюсь стало понятнее почему они такие разные):


Model View Controller


А теперь самое главное — как применяются, что обозначают и чему соответствует Модель Вид и Контроллер при написании приложений?


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


«Трехуровневый MVC» от веб


Первый подход идет из веб-программирования, где MVC получил самое широкое распространение, и поэтому в нем максимально отразились свойственные веб-программированию черты. А именно, привязка к трехуровневой архитектуре «клиент–сервер–база данных» и преобладание скриптовых языков. В результате компоненты MVC формально привязываются к трем слоям архитектуры и получается что:


  1. Модель = База Данных
    Модель — это просто данные, которыми оперирует приложение


  2. Контроллер = Сервер
    Контроллер — это бизнес-логика приложения. Иногда еще говорят что контроллер это центр обработки всех запросов и принятия решений, а также промежуточный слой обеспечивающий связь модели и представления.


  3. Вид = Клиент (как правило тонкий)
    Вид — это пользовательский интерфейс. Причем интерфейс в этом случае, как правило, понимается в основном исключительно как «дизайн», просто набор графических элементов. Логика же работы этого интерфейса, как и логика работы с данными, выносится в Контроллер

image alt text


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


  1. Независимость Модели является главным в MVC. Если Модель тонкая, то есть содержит лишь данные, то возможность ее независимой разработки имеет мало смысла. Соответственно при таком подходе теряет смысл и сам MVC


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


  3. На практике Контроллеру в веб-приложении обычно соответствует один скрипт и вынесение всей бизнес-логики в Контроллер фактически означает еще и то, что большая часть приложения оказывается в одном скрипте. Отсюда и появился термин ТТУК — толстый тупой уродливый контроллер


  4. Поскольку, как правило, тонкой является не только Модель, но также и Вид (тупой Вид или тупой интерфейс — Dump GUI), то, как следствие, в Контроллер помимо всей бизнес-логики приложения помещается также еще и логика управления пользовательским интерфейсом. То есть, вместо разделения бизнес логики и логики представления при таком подходе получается их смешение.

web MVC


Программа, конечно, разбивается на множество MVC, соответствующих страницам веб-приложения, и это спасает ситуацию но, увы, не меняет сути. Проблема эта известна, вот неплохая статья — «RIA Architecture».


Типичные ошибки: смешение в Контроллере бизнесс-логики и GUI-логики

Хорошая новость заключается в том, что «веб-вариант MVC», всего несколько лет назад бывший самым распространенным, сейчас активно сдает позиции. Плохо то, что он по прежнему распространен, только теперь не в явном, а в замаскированном виде. Поскольку за фразы (цитирую):»Модель это обмен данными с БД и т.п. Контроллер логика обработки этих данных и подготовка к View» сейчас активно «минусуют», то стали писать:


  • Модель — это данные и методы работы с ними
  • Контроллер — обработка действий пользователя и вводимой им информации

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


«Архитектурный MVC»


Второй подход гораздо ближе к первоисточникам. Поэтому разберем его подробнее.


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


Архитектурные принципы мы постарались собрать и описать в статье «Создание архитектуры программы или как проектировать табуретку». Если же говорить предельно кратко, то суть состоит в следующем: сложную систему нужно разбивать на модули. Причем декомпозицию желательно делать иерархически, а модули, на которые разбивается система, должны быть, по возможности, независимы или слабо связаны (Low coupling). Чем слабее связанность, тем легче писать/понимать/расширять/чинить программу. Поэтому одной из основных задач при декомпозиции является минимизация и ослабление связей между компонентами.


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


»1» Отделение модели предметной области (бизнес логики) приложения от пользовательского интерфейса


Первая и основная идея MVC заключается в том, что любое пользовательское приложение в первом приближении можно разделить на два модуля — один из которых обеспечивает основной функционал приложения, его бизнес логику, а второй отвечает за взаимодействие с пользователем:


MVC


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


Задача же взаимодействия с пользователем выносится в отдельный модуль — пользовательский интерфейс и тоже может решаться относительно независимо.


Именно модель предметной области (Доменная Модель от английского domain model) считается Моделью в «архитектурном MVC» (отсюда и термин). Поэтому так важно чтобы она была независимой и могла независимо разрабатываться и тестироваться.


«Сердцевиной идеей MVC, как и основной идеей для всех последующих каркасов, является то, что я называю «отделенное представление» (Separated Presentation). Смысл отделенного представления в том, чтобы провести четкую границу между доменными объектами, которые отражают наш реальный мир, и объектами представления, которыми являются GUI-элементы на экране. Доменные объекты должны быть полностью независимы и работать без ссылок на представление, они должны обладать возможностью поддерживать (support) множественные представления, возможно даже одновременно. Этот подход, кстати, так же был одним из важных аспектов Unix-культуры, позволяющий даже сегодня работать во множестве приложений как через командную строку, так и через графический интерфейс (одновременно).» — Фаулер


»2» Независимость Модели и синхронизация пользовательских интерфейсов за счет шаблона Наблюдатель


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


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


»3» Разделение Пользовательского Интерфейса на Вид и Контроллер.


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


MVC


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


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


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


«Original MVC»: Реенскауг и SmallTalk-80


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


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


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


Остается второй вариант — Модель это множество доменных объектов, совместно реализующих бизнес логику. Это предположение подтверждает и сам Реенскауг:»A model could be a single object (rather uninteresting), or it could be some structure of objects.» Но тогда остается открытым вопрос — кто реализует шаблон Наблюдатель, откуда берет данные Вид, куда передает команды пользователя Контроллер?


И вот здесь нередко встречается попытка обмануть самих себя путем примерно следующего рассуждения: «пусть Модель это множество доменных объектов, но… среди этого множества есть в том числе и «объект с данными», вот он-то и будет реализовывать шаблон Наблюдатель, а также служить источником данных для Вида.» Эту уловку можно назвать «Модель в Модели». И по сути это еще один «завуалированный» вариант того, что «Модель это данные».


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


Для того же, чтобы понять «а как должно быть» предлагаю вновь обратится к «принципам». Когда говорилось о том, что систему надо разбивать на модули, слабо связанные друг с другом, мы не упомянули главное правило, позволяющее добится этой самой слабой связанности. А именно — модули друг для друга должны быть «черными ящиками». Ни при каких условиях один модуль не должен обращаться к объектам другого модуля напрямую и что либо знать о его внутренней структуре. Модули должны взаимодействовать друг с другом лишь на уровне абстрактных интерфейсов (Dependency Inversion Principle). А реализует интерфейс модуля как правило специальный объект — Фасад.


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


Ну, а теперь схема из доклада Трюгве Реенскауга:


MVC Reenskaug


Пояснение: Поскольку во времена создания MVC интерфейсы компьютерных программ были в основном текстовыми, то есть, по сути представляли собой простейший вид редактора, то вместо термина «Пользовательский Интерфейс», который появился позже, Трюгве Реенскауг использует термин «Editor» (редактор).


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


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


И если уж рисовать схему MVC, то выглядеть она должна следующим образом:


MVC


Давайте рассмотрим данную схему подробнее. Традиционно в клиент серверных приложениях главным считается сервер. Он предоставляет услуги/сервисы и решает в каком виде это должно быть реализовано. Соответственно интерфейс и фасад, как правило, определяются с точки зрения сервера. А клиенты под этот заданный формат подстраиваются.


На практике же более адекватной оказывается не сервер-ориентированная архитектура, а клиент-ориентированная. В ней фокус с сервера смещается в сторону клиента и интерфейс, вернее интерфейсы (и фасад или фасады), определяются исходя из потребностей клиентов. Вместо Предоставляемого Интерфейса (Provided Interface) используются Требуемые Интерфейсы (RequiredInterface).


RequiredInterface


Конкретные реализации могут варьироваться, но это не суть важно


RequiredInterface


Клиент ориентированный подход гораздо лучше соответствует Принципу разделения интерфейсов (Interface Segregation Principle) поскольку в нем вместо единого для всех толстого ProvidedInterface используется множество тонких RequiredInterface.


Если я не ошибаюсь, именно такой подход используется в архитектуре микросервисов. Там для взаимодействия с множеством сервисов введено понятие шлюза, который является ни чем иным как фасадом — »An API Gateway is a server that is the single entry point into the system. It is similar to the Facade pattern from object-oriented design. The API Gateway encapsulates the internal system architecture and provides an API that is tailored to each client.» Building Microservices: Using an API Gateway.


Причем шлюз этот »вместо того чтобы обеспечивать общий единый для всех API, предоставляет различные API для каждого клиента (Rather than provide a one-size-fits-all style API, the API gateway can expose a different API for each client. For example, the Netflix API gateway runs client-specific adapter code that provides each client with an API that«s best suited to it«s requirements)» API Gateway.


Как мы увидим дальше клиент-ориентированный подход применялся также и в SmallTalk-80. Но вначале давайте просто пере-рисуем схему MVC с учетом вышесказанного:


MVC


Смотрим на фасад… Вот он тот самый клей (glue), объект посредник, прокси, фильтр, адаптер… связывающий между собой доменную модель и пользовательский интерфейс и поставляющий нужные данные в нужном/удобном формате.


Удивительно то, что кроме Реенскауга об этом почти никто не пишет. Хотя некоторые пере-открывают эту идею самостоятельно (пример можно посмотреть тут или тут раздел «Interface-Based Programming Techniques»).


Особенно хорошо тема Моделей-интерфейсов раскрыта в статье одного из JavaGuru — Advanced MVC Patterns. Автор подчеркивает, что Модели это не данные, а исключительно интерфейсы/объекты-посредники/фильтры (Models as Proxies, Models as Filters), обеспечивающие удобный доступ к данным, которые могут находится где угодно — на разных машинах, в разных форматах:»О чем большинство программистов не думает, так это о том, что модели являются всего лишь интерфейсами. Они не должны содержать никаких данных!… Модели-посредники расширяют охват и позволяют использовать уже существующие данные где бы они не находились».


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


На форумах нередко встречается вопрос — «Чем контроллер отличается от фасада?». Не смотря на наивность этот вопрос вполне закономерен и на него сложно дать разумный ответ поскольку во многих MVC фреймворках Контроллер на самом деле фактически является фасадом — «Фронт-Контроллер».


Чем плохо такое решение? Если оно граммотно реализовано, то ничем. Но это в теории. А на практике нередко происходит путаница концепций и понятий и в результате Фронт-Контроллер с одной стороны злоупотребляет своими полномочиями и вместо делегирования команд начинает включать в себя реализацию бизнес логики. А с другой — продолжает одновременно выполнять функции пользовательского интерфейса и в результате в нем происходит уже упоминавшееся смешение «бизнес логики» и «GUI логики» (что собственно и&nb

© Habrahabr.ru