Навигация
Модели, представления и контроллеры
У Rails есть одна интересная особенность — она накладывает ряд серьезных ограничений
на структуру ваших веб-приложений. Как ни удивительно, но эти ограничения
в значительной мере облегчают создание приложений. Давайте посмотрим,
почему так происходит.
Модели, представления и контроллеры
В далеком 1979 году Трюгве Реенскауг (Trygve Reenskaug) придумал новую архитектуру
для разработки интерактивных приложений. По его замыслу приложения
разбиваются на компоненты трех типов: модели, представления и контроллеры.
Модель отвечает за поддержку состояния приложения. Иногда это состояние
является кратковременным, продолжающимся только на время нескольких взаимодействий
с пользователем. А иногда состояние является постоянным и сохраняется
вне приложения, чаще всего в базе данных.
Модель — это больше, чем просто данные; в ней прописаны все бизнес-правила,
применяемые к этим данным. К примеру, если скидка не должна применяться
к заказам стоимостью менее 20 долларов, моделью будут предписаны соответствующие
ограничения. И в этом есть свой определенный смысл. Помещая реализацию
бизнес-правил в модель, мы гарантируем, что ничто иное в приложении не
может их испортить. Модель работает и сторожем, и хранителем данных.
Представление отвечает за формирование пользовательского интерфейса, который
обычно основан на данных модели. Например, интернет-магазин должен
иметь перечень товаров для отображения на экране каталога. Этот перечень будет
доступен через модель, но получать его у модели и форматировать для конечного
пользователя будет представление. Хотя представление может преподносить
пользователю различные способы ввода данных, оно никогда не занимается
их непосредственной обработкой. Работа представления завершается, как только
данные будут отображены. Допускается существование множества представлений,
имеющих доступ к одним и тем же данным модели, часто с различными
целями. В интернет-магазине может быть представление, отображающее информацию
о товаре на странице каталога и другой набор представлений, используемых
администраторами для добавления товаров или редактирования сведений
о них.
Контроллеры организуют работу приложения. Они воспринимают события
внешнего мира (обычно ввод данных пользователем), взаимодействуют с моделью
и отображают соответствующее представление для пользователя.
Этот триумвират — модель, представление и контроллер — формирует архитектуру,
известную как MVC (Model-View-Controller — Модель-Представление-
Контроллер). На рис. 2.1 MVC показана в абстрактном представлении.
Первоначально MVC была предназначена для обычных GUI-приложений,
при создании которых разработчики обнаружили, что разделение ведет к значительному
уменьшению количества необходимых увязок, благодаря чему облегчаются
написание и поддержка программного кода. Каждое понятие или действие
находило свое выражение лишь в одном, хорошо известном месте. Применение
MVC походило на строительство небоскреба при наличии уже готовых перекрытий
— куда проще навесить все остальные части, располагая готовой структурой.
В программном мире мы нередко игнорируем хорошие идеи из прошлого, поскольку
мчимся навстречу будущему сломя голову. Когда разработчики впервые
приступили к созданию веб-приложений, они вернулись назад, к написанию монолитных
программ, в которых в один огромный программный код смешивались
представление, доступ к базе данных, бизнес-логика и обработка событий. Но
идеи из прошлого медленно подкрадывались сзади, и люди начинали экспериментировать
с архитектурами для веб-приложений, которые являлись зеркальным
отражением идей MVC двадцатилетней давности. В результате появились
такие исполнительные среды, как WebObjects, Struts и JavaServer Faces. Все они
в различной степени основывались на идеях MVC.
Ruby on Rails также относится к среде выполнения, основанной на идее MVC.
Средой Rails предписывается структура вашего приложения: вы разрабатываете
модели, представления и контроллеры как различающиеся по функциональным
возможностям фрагменты, а она связывает их вместе при выполнении вашей
программы. Одно из положительных свойств Rails заключается в том, что связывание
процессов основано на использовании разумных установок по умолчанию,
поэтому для того, чтобы все заработало, вам обычно не приходится создавать какие-
либо внешние конфигурационные метаданные. В этом выражается философия
Rails, в которой отдается предпочтение соглашению, а не настройкам конфигурации.
В Rails-приложениях входящие запросы сначала посылаются маршрутизатору,
который решает, куда в приложении запрос должен быть послан и как, собственно,
этот запрос должен быть разобран. В конечном счете, на этой стадии где-то
в коде контроллера определяется конкретный метод (называемый на языке Rails
действием). Действие может рассмотреть данные, непосредственно находящиеся
в запросе, может вступить во взаимодействие с моделью, а также может привести
к вызову другого действия. В итоге действие готовит информацию для представления,
которое отображает что-нибудь для пользователя.
На рис. 2.2 показано, как Rails обрабатывает входящий запрос. В этом примере
приложение предварительно отображало страницу каталога товаров, и пользователь
только что щелкнул на кнопке Добавить в корзину (Add То Cart), которая следовала
за наименованием одного из товаров. У этой кнопки имеется ссылка на
адрес http://my.url/store/add_to_cart/123, в которой add_to_cart означает действие
в нашем приложении, а 123 является внутренним идентификатором выбранного
товара1.
Компонент, занимающийся маршрутизацией, получает входящий запрос и немедленно
разбивает его на части. В нашем простейшем случае он берет первую
Мы рассмотрим формат URL, используемых в Rails, чуть позже. Тем не менее именно
здесь стоит заметить, что использование URL, вызывающих действие наподобие добавления
в корзину, может быть небезопасным. Подробности см. в разделе 21.6, «Проблемы,
связанные с GET-запросами».
часть пути, store, в качестве имени контроллера, а вторую часть, add_to_cart,
а качестве наименования действия. Последняя часть пути, 123, в соответствии
с соглашением извлекается во внутренний параметр, названный i d. В результате
подобного анализа маршрутизатор знает, что ему необходимо вызвать метод
=dd_to_cart в классе контроллера StoreController (мы ведем речь о соглашениях
по наименованиям, рассмотренным в разделе 14.4, «Соглашение об именах
»).
Метод add_to_cart обрабатывает запросы пользователей. В данном случае он
отыскивает покупательскую корзину текущего пользователя (которая является
объектом, управляемым моделью). Он также запрашивает модель о поиске информации
по товару 123. Затем он предписывает корзине покупателя добавить
к своему перечню данный товар. (Проследите за тем, как модель используется
для отслеживания всех бизнес-данных; контроллер предписывает, что надо делать,
а модель знает, как это сделать.)
Теперь, когда в корзину положен новый товар, мы можем показать ее пользователю.
Контроллер организует работу так, что представление получает доступ
к объекту c a r t (корзина) от модели, а затем вызывается код представления. В Rails
этот вызов зачастую заранее подразумевается; и опять мы видим, что соглашения
помогают связать конкретное представление с заданным действием.
Вот и все, что имеет отношение к веб-приложению MVC-архитектуры. Следуя
набору соглашений и соответствующему разделению функциональности, вы
обнаружите, что с вашим кодом становится проще работать, а приложение будет
легче расширять и поддерживать. Все это напоминает хорошо организованную
коммерческую деятельность.
Если MVC справляется с задачей соответствующего разделения вашего кода,
то может возникнуть вопрос, зачем тогда нужна такая среда выполнения, как
Ruby on Rails? Ответ здесь прост: Rails ведет за вас все низкоуровневое «хозяйство
», справляясь с той массой деталей, управление которой отняло бы у вас уйму
времени, и дает возможность сосредоточиться на основной функциональности
приложения. Посмотрим, как это происходит...