Есть данные для хранения, данные для отображения, и есть код. Почему под словами Модель или Вью в mvc понимают фрагменты кода? Тут какая-то путаница.

Что такое Контроллер mvc в вебе?

MVC – Модель, Контроллер, Вью (Представление). Вместо мутного «Модель» используем «Данные». И попытаемся определить откуда у всего этого ноги растут.

Данные – это что-то, что хранится в компьютере; Представление – то, что получается из данных при выводе на экран; Контроллер – ? – ну, по-видимому, все части системы, которые переводят данные в нужное Представление (то есть всё, что преобразует Данные).

В такой широкой интерпретации Контроллер теряет смысл (как говорят специалисты, превращается в Толстый Тупой Уродливый Контроллер). Но как иначе определить части системы?

Наше определение в общем-то правильно. Оно основано на изначальном делении Информационной Системы на Клавиатуру (Данные), Процессор (Контроллер) и Монитор. Вы нажимаете на Клавиатуре клавишу с надписью «Ы» – в систему попадают Данные. Процессор что-то там делает, и на Мониторе вы видите Представление буквы «Ы» – в виде буквы «Ы» или, если локализация неправильная, в виде какой-нибудь кракозябры или бекараса (названного так по имени французского пианиста 19 века Эжена Бекара, известного своими чудачествами). Что здесь не так?

Здесь всё правильно, но не имеет практического смысла. Потому что не существует такого человека, который мог бы управлять всем этим сразу. А создание системы, разделяющей сущности на Данные и Представление, призвано как раз помочь определённому человеку – программисту. А в нашей системе ТТУК состоит как из программ, так и из разных железок (например, если буквы надо вывести не на экран, а на принтер, наш Контроллер станет ещё уродливее и толще). Попробуем сузить сферу влияния до такой степени, чтобы она стала более-менее обозримой и подвластной одному человеку.

Как на экране появляются Представления? (Кто крайний?) От операционной системы. Это точно не наш клиент. Идём дальше. Как Представления данных попадают в операционную систему? В нашем случае – от веб-клиента (браузера). Это уже ближе. А Данные попадают в Систему по-прежнему с клавиатуры, только появляется дополнительное условие: промежуток времени между вводом данных и выводом их в Представление. В течение этого промежутка Данные должны где-то храниться. Например, в базе данных. Или в файле index.html.

Итак, вот первый вариант нашей системы Данные – Контроллер – Представление: файл index.html, содержащий текст «Очень важная информация» (на сервере или на локальной файловой системе – пока неважно), и «читалка» – браузер (хотя, строго говоря, браузер в интересующую нас систему не входит). Можно открыть браузером файл и вывести на экран информацию. Попробуем теперь ответить на основной вопрос философии: где здесь мы, программисты?

Пока нет конфликта между вводом и выводом данных (нажал букву «Ы» – увидел через некоторое время букву «Ы»), веб-программист не нужен. Конфликт же возникает очень быстро, при самом небольшом усложнении текста. Например: «Очень важная информация. [абзац] Параграф первый». Если бы речь шла не о вебе, а о хранении данных в «Блокноте», ничего бы не случилось: нажал «Энтер» и получил Абзац. Открыл в следующий раз текст в том же «Блокноте» – и увидел тот же Абзац.

Но в вебе «Энтер» не создаёт (в общем случае) Абзац, где бы мы ни хранили текст – в базе данных или в файле index.html. Абзац на веб-странице можно создать, например, добавив в нужное место последовательность символов <br>. А это уже требует специальных знаний, требует некоторого программирования. Допустим, автор ввёл в наш файл текст в обычном стиле «Блокнота», создав абзацы с помощью двойного нажатия «Энтер»: «Очень важная информация.\r\n\r\nПараграф первый». Тогда веб-программист для создания правильного Представления должен будет добавить в файл index.html примерно следующий код (заменяющий «естественные» Абзацы – Энтеры – на нужные HTML тэги):

Код 1.

Пробуем зафиксировать схему происходящего. Есть Данные – какой-то текст в файле index.html; есть Представление – какой-то текст (перемешанный с кодом?), отдаваемый в конечном итоге браузеру для вывода на экран, этот текст в нашем случае будет выглядеть так:

И что-то должно быть у нас Контроллером. По идее, это всё, что преобразует Данные из «хранимого» вида в «отображаемый». То есть у нас это Код 1.

Почему при общении через Блокнот не нужно знать о Данных и Представлении (и не нужен никакой Контроллер)? Потому что автор пишет, как хочет в программе Блокнот, а читатель потом в той же программе читает (видит) ровно то, что написал автор. Контроллер, однако, тут всё же есть – это сама программа Блокнот. Отсюда неизбежно вытекает следующий вопрос: а почему нельзя в вебе сделать так же: запрограммировать «раз и навсегда» программу «Браузер», чтоб она работала, как Блокнот: автор пишет на сайте, как хочет, а читатели читают (видят) ровно то, что написал автор?

Этот вопрос мучает не только простых пользователей – тыщи (или уже сотни тысяч?) веб-разработчиков пытаются создать эту универсальную программу (чаще всего называемую обобщённо CMS), в которой было бы так же просто работать со страницами веб-сайта, как с текстовыми файлами в Блокноте.

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

Структура Контроллера

Почему же наш Контроллер (Код 1) – толстый тупой и уродливый? Чем он плох? Один из вариантов ответа заключается в том, что у нас Контроллера вообще нет, а Код 1 относится к Представлению, и называют его в этом случае Шаблонизатором. То есть некоторых теоретиков веб-программирования наша простая структура (данные на входе, изменённые данные на выходе, а всё, что между ними, – Контроллер) не устраивает, и они начинают распиливать Контроллер на кусочки программного кода, относящиеся, например, к извлечению Данных (Модель) и на кусочки, оформляющие непосредственно Отображение (Шаблонизаторы). А между этими кусочками остаётся уже непосредственно Контроллер – худой, умный и красивый. То есть, в приближении к идеалу, вообще ничего не остаётся.

Так, конечно, бывает редко. Если на сайте больше одной страницы, Контроллер нужен, как минимум, для того, чтобы обеспечить «переключение» между страницами. Допустим, наш автор добавил к Важной Информации следующее замечание: «\r\n\r\nПараграф два. Подробности на Второй странице (index2.html)». Тогда в Контроллер, в функцию patch надо будет добавить примерно следующий код (превращающий некоторые фрагменты текста в гиперссылки):

Код 2.

Если страниц на сайте больше пяти, автор уже может не успеть (или забыть) упомянуть всех в тексте Данных явно. Тогда на какую-то из страниц ссылки не будет, и читатели о ней не узнают. Тут уже возникает необходимость в Настоящем Переключающем Контроллере, который полностью снимает с автора заботу об организации меню, то есть системы переходов между страницами на сайте (подробно описано в статье http://dn.ir2.ru/mvc0.aspx).

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

Здесь есть простые принципы (такие же простые, как «общий Контроллер»), не обязательно требующие распила Контроллера на Шаблонизатор и CRUD. В нашем примере плохо то, что код скрипта находится прямо в файле с Данными. Мало того, что этот код попадает в Представление, – код Контроллера автор как-то должен запихать в файл index.html. А раз мы добавили на сайт ещё одну страницу (index2.html), автор должен будет добавить точно такой же скрипт и на неё. Это плохо, даже если добавлять будет не автор (а программист будет ходить после автора по всем страницам и пихать туда скрипты).

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

А в файл site.js поместить уже непосредственно код «контроллера». Если каждую новую страницу на сайте создавать по такому шаблону, программисту будет легко управлять сайтом, изменяя код только в одном месте; а браузеру будет легче выводить на экран страницы, не перегруженные ненужным программным кодом. Фигли толку говорить о каких-то тонкостях «активных шаблонов» и «компонентного подхода», если на практике большинство сайтов тупо перегружено javascript-кодом (перемешанным с такстом страниц). Они, впрочем, так же перегружены и перемешанным с данными php-кодом, только на стороне клиента этого не видно.

Что было раньше (выявление зависимостей)?

Второй принцип (после отделения кода от данных) – построение очерёдности, выяснение, что из чего следует. Теоретики любят повторять, что Модель (Данные) ни из чего не следует (ничего «не знает» о Представлении и о Контроллере). Возьмём пример: таблица каких-нибудь запчастей (или автошин). Автор создал таблицу в БД, и она как-то отправляется пользователю. У пользователя есть возможность сортировать эту таблицу, щёлкая по заголовкам столбцов. Пока способ сортировки определяется автоматически, всё хорошо. Но если надо, чтобы какой-то из столбцов сортировался особо (например, строка как число – такое иногда бывает, например, при работе с валютой), наступает вопрос: как Представление узнает об этом?

В Представление надо будет вписать HTML-код (в заголовке таблицы), что-то вроде: <th axis='num'> (tabsort003.aspx). В норме этот код вычисляется Контроллером (или функциями Модели?) автоматически, на основании распознавания типов данных в БД. Но как вписать необычный код, который нельзя вычислить автоматом? Данные это или метаданные, но они должны быть где-то записаны автором (автор должен явно выразить своё намерение относительно параметров Представления). То есть, получается, Модель в этом случае делает предположение о Представлении (допускать, что там будет таблица с возможностью сортировки), и на всякий случай принимает от автора дополнительное описание для некоторых столбцов таблицы. Автор, конечно, может ничего не знать о каких-то там axis, но у него должна быть кнопочка в CMS, которой можно отметить «Исключить столбец из сортировки» или «Назначить такой-то способ сортировки».

Другой пример – валидация HTML-формы (php2js_control.aspx). Допустим, php скрипт генерирует HTML код формы, и в коде кнопки «Сохранить» пишет: <input type='button' onclick='validate(this.form)' value='Сохранить'>. Откуда php знает, что в js существует такая функция – validate? Такие моменты (когда надо «руками» синхронизировать разные части Контроллера), называют «неявными зависимостями». В приведённом случае, если php навешивает на элемент javascript обработчик, так сам же php и должен генерировать код обработчика (из php создавать файлы js). Но правильнее, конечно, чтобы php в данном случае вообще «не знал» о javascript: <input type='button' value='Сохранить'> – и всё. А если нужна валидация, javascript сам «найдёт» форму и прицепит к ней необходимые обработчики после загрузки страницы в браузер:

В заключение

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

D.M., admin

Комментарии

Дилетант 09.08.2013 21:06:11

Может, проведём голосование?

ankhzet 02.08.2013 07:58:25

"бред" Поддерживаю.

dfsdf 29.01.2013 11:26:47

бред