Большинство сложных программных систем создаются с использованием паттерна MVC.
Многие программисты, приходя на javascript с других языков, по инерции используют этот подход.
Однако, при программировании javascript-интерфейса он зачастую бесполезен, приводит к тормозам, переусложнению приложений...
В javascript-интерфейсах, в отличие от Java/C++ и других - обычно не нужен паттерн MVC.
Применение паттерна MVC заключается в разделении собственно данных, их отображения и обработки действий пользователя по трем компонентам.
Между компонентами есть взаимодействия, изображенные стрелочками на рисунке (один из устоявшихся вариантов MVC).
Главный тезис статьи можно сформулировать так:
Controller пусть остается отдельно, а Model от View отделять не надо. Особенности Javascript/DOM/CSS позволяют успешно реализовывать аспекты Model средствами View.
Дальнейшая часть статьи - обоснование с примерами, почему именно так, а не иначе, и почему устоявшаяся практика MVC для javascript-интерфейсов работает плохо.
Никто не говорит о том, что MVC вообще - это плохо. Наоборот, MVC - это хорошо. Но вся штука в том, что при программировании для Web есть как минимум 3 важных особенности.
Паттерн MVC увеличивает количество кода за счет появления дополнительных межкомпонентных коммуникаций. То есть, не "сделай это", а "попроси компонент XXX сделать это и обеспечь его данными".
Будем исходить из предположения, что разрабатываем сложный интерфейс.
В самом деле, если интерфейс заведомо простой (и таким останется) - то лишние паттерны тем более не нужны.
Javascript/DOM в браузере - не самая быстрая платформа, а приложения фактически являются real-time, т.е интерфейс должен реагировать и отображаться по возможности мгновенно, плавно, и не сильно жрать память.
Межкомпонентные коммуникации Model-View добавляют тормозов. Это повод их убрать.
Далее, и модель и вид в javascript, как правило, оперируют данными в одинаковом формате.
То есть, значение в форме input.value - не рисованный объект типа GraphicsTTFString, как это может быть в обычном десктоп-програмировании, не кодированная строка пикселей, а обычный текст.
Поэтому отделение Model от View приводит к излишнему дублированию данных.
Что такое View в javascript-интерфейсах?
Это в первую очередь DOM и CSS. Модель DOM заранее включает в себя следующее:
var div = document.createElement('div')
div.myProperty = 123
//.. и даже ...
div.myMethod = function() {
alert(this.innerHTML)
}
document.getElementById. Сложные CSS-селекторы и XPath постепенно приходят в браузеры, и успешно эмулируются JS-библиотеками.При этом основные аспекты оформления задаются вообще отдельно от DOM, при помощи CSS.
Вы видите, DOM - это совсем не тот "классический" View, который обычно имеется в виду. Он гораздо мощнее.
Так зачем нам поддерживать иерархию, средства выборки и контейнеры для свойств в специальных javascript-объектах Модели, если с этим замечательно справляется DOM/View ?
Минусы, как и плюсы, связаны с производительностью.
При работе исключительно с javascript, работа идет в едином пространстве интерпретатора javascript. Это куда быстрее, чем обращаться к DOM-объектам, который в Microsoft относятся к COM, в Firefox - к XPCOM, в общем - живут отдельно от javascript-движка.
Эта проблема раньше была очень актуальна для Internet Explorer.
На момент написания статьи она фактически решена как библиотечными средствами (clobbering, учет и зачистка назначаемых DOM-элементам свойств), так и патчами.
В частности, как в IE7, так и в IE6 с обновлениями середины 2007 связка DOM-javascript почти не течет.
При разработке приложений надо смотреть, насколько тесно изменение ее модели (данных) завязано на изменение представления (DOM).
Если работа с объектом преимущественно не требует обновления внешнего вида, то можно подумать о выделении модели в целях производительности.
Однако обычно обновление данных тесно завязано с обновлением их вида, и это еще один довод к тому, чтобы их не разделять.
Что касается утечек памяти - в зависимости от аудитории и типа вашего ресурса, вы можете использовать специальные библиотеки и практики кодирования, либо просто игнорировать эту проблему.
Современные js-фреймворки (YUI, dojo, jQuery) ликвидируют бОльшую часть утечек памяти.
Полностью впихивать Model во View, конечно, не обязательно. Да оно обычно и не нужно.
Можно выделить три устоявшихся практики.
Создается единый объект, который существует над компонентами интерфейса и хранит данные всех моделей, задействованных в данном интерфейсе.
Например, во вложенном меню это будет единый объект Menu, который умеет show/hide любые подменю. При этом подменю как таковое не является javascript-объектом.
Аналогично, в javascript-дереве это может быть единый объект Tree, который манипулирует узлами, а сами узлы - просто элементы DOM и хранят данные непосредственно в DOM.
Существует единый объект, который при небольшой "доинициализации" может выполнить роль любой из однотипных компонент интерфейса.
Например, рассмотрим контекстное меню для javascript-дерева, которое вызывается правым кликом мыши на элементе.
Вместо пачки меню - для каждого элемента дерева своё, создается единый объект ContextMenu.
При клике на узел:
Приспособление ContextMenu к конкретному элементу выполняется очень просто. В данном случае - не нужно создавать DOM-структуру меню, она и так есть. Не надо и создавать новый объект со своими методами - достаточно тех что есть.
Наконец, иногда целесообразно частично отделить некоторые аспекты Model от View.
Обычно при этом выводится в модель то, что во View хранить нельзя или неудобно, так чтобы не возникало дублирование.
В этой статье содержатся те мысли и подходы, которые отличают профессионального JS-программиста от обычного десктоп-кодера, подсевшего на javascript.
Их применение ведет к уменьшению и упрощению кода. Успехов.