Применение к Web принципов объектно-ориентированной разработки может оказаться весьма затруднительным. Многие из современных Web-технологий предполагают (или даже требуют) использование некорректных решений. Работа со скриптами и серверными страницами провоцирует повторное использование фрагментов в стиле «вырежи и склей», низкоуровневое кодирование непосредственно в базах данных, слабое структурирование программ. Компонентные модели наподобие Component Object Model (COM) и Enterprise JavaBeans (EJB) предусматривают создание строительных блоков для сборки приложений, но при этом приходится жертвовать многими преимуществами, которые могли бы дать объекты. XML особое значение придает платформно-независимому многократному и совместному использованию контента и сообщений, но заставляет расплачиваться за это возможностью инкапсуляции и увязывания поведения с состоянием ключевых черт объектно-ориентированного подхода.
Языки скриптов, широко применяемые в Web, зачастую оптимизированы таким образом, чтобы быстро реализовывать простую функциональность, а не для модульного конструирования больших программ. Кроме того, для таких языков, как правило, не существует мощных сред разработки, привычных для языков общего назначения. Некоторые Web-разработчики даже умышленно игнорируют принципы инженерии программ. Они утверждают, что к написанию скриптов неприменимы методы проектирования (объектные или какие-либо иные), которые используются в «реальных» системах.
Однако объектно-ориентированные методы приобретают все большее значение в Web-разработке по мере того, как Web-приложения становятся все сложнее и интегрируются с традиционными серверными приложениями. Анализ ключевых Web-технологий позволяет выявить проблемы, которые возникают, если технологии эти применять «как есть». Описываемая многоуровневая объектно-ориентированная архитектура на основе шаблона Model-View-Controller (MVC) позволяет разрешить часть из этих проблем при создании крупных, структурированных систем.
Мотивировка
Если попытаться взглянуть на скрипты с точки зрения объектно-ориентированного подхода и многоуровневой архитектуры, то бросается в глаза, что один скрипт имеет дело сразу с несколькими уровнями обработки. Скрипт: принимает входные данные; реализует логику приложений; реализует бизнес-логику; формирует выходные данные (логика презентации).
Смешение уровней затрудняет изменение или тестирование любого отдельного аспекта функционирования. Кроме того, серьезные проблемы возникают в связи с поддержкой.
Получение входных данных
При получении входных данных скрипту передается либо необработанный входной поток HTTP, либо его представление после минимального синтаксического разбора. HTTP поддерживает несколько различных механизмов передачи параметров (URL, значения запросов, формы); все эти данные предаются как простые цепочки символов. Каждый скрипт должен знать или сам задавать механизм передачи параметров, преобразования параметров в значения соответствующего типа и проверки их корректности. Это неизбежно ведет к многократному дублированию кода.
Поддержка логики приложений
Еще один аспект, влияющий на ввод и логику приложений, — отсутствие упрятывания информации при обращении к данным запроса или сеанса. Скрипт извлекает входные данные из запроса по имени. HTTP — это протокол, который не сохраняет состояние, поэтому данные, используемые в нескольких скриптах, надо либо сохранять в связанном с пользователем сеансе, либо всякий раз считывать из внешнего источника данных в тех скриптах, которым они требуются. Скажем, если скрипт передает информацию для регистрации в системе в виде данных формы, код, сохраняющий эту информацию в сеансе, может выглядеть так:
password = request.getParameter («passwordField «); decrypted = this.decode (password); request.getSession().putValue («password «,decrypted);
И хранилище в сеансе, и хранилище во внешнем источнике данных, по существу, доступны отовсюду, и приложение обращается к ним как к словарю, используя соответствующие цепочки символов в качестве ключей. Обычные механизмы программирования для управления доступом к переменным к ним не применяются, и любые скрипты или серверные страницы, которые хотят использовать эти данные должны знать о соглашениях об именовании. Не так-то просто найти все обращения к переменным с помощью механизмов языков программирования, поэтому любая модификация программы становится еще сложнее. Если соглашения об именовании не инкапсулированы в самом скрипте, то информация о них и связанные с протоколом HTTP детали могут оказаться размазаны по всему приложению. Это мешает последующей адаптации скрипта к новым требованиям при необходимости его нового применения. Более того, это провоцирует появление синтаксических ошибок и разных скриптов, использующих одни и те же имена для разных целей. По мере роста числа скриптов и серверных страниц эти проблемы становятся все серьезнее.
Если для поддержки логики приложений использовать серверные страницы, то иногда приходится добавлять к странице довольно много кода. Существующие методы управления кодом, как правило, не применимы к коду внутри серверных страниц. Несмотря на множество предложенных методов, отладка кода внутри серверных страниц крайне затруднена. В этом случае для создания кода и отладки не всегда можно использовать возможности современных сред разработки. По этим причинам было бы неплохо минимизировать количество кода на серверных страницах и хранить логику приложений вне этих страниц.
Поддержка бизнес-логики
Поскольку весь код группируется вместе, трудно изолировать бизнес-логику от других уровней, особенно от логики приложения. Более того, если нет возможности выполнять фрагменты скриптов по отдельности, то не удастся и независимо тестировать бизнес-логику (как и любые другие уровни).
При использовании серверных страниц бизнес-логика порождает те же самые вопросы, которые обсуждались в пункте, посвященном логике приложений. Возможно, к страницам придется добавить большое количество кода, но даже страницами с минимумом кода трудно управлять и их очень сложно отлаживать.
Генерация выходных данных
При формировании выходных данных простые скрипты объединяют HTML-код результатов с динамическими данными. Модификация внешнего вида сайта, как и адаптация приложения к особенностям нескольких различных устройств отображения все усложняется. Особенно это становится существенно по мере расширения возможностей доступа в Web, в частности, посредством поддерживающих протокол WAP мобильных телефонов и иных компактных устройств.
Серверные страницы помогают решить последнюю проблему, позволяя Web-разработчикам проектировать и сопровождать, а программистам — аннотировать страницы.
Model-View-Controller
Чтобы преодолеть все эти трудности, можно воспользоваться комбинацией скриптов, серверных страниц и кода приложений. Предлагаемый в статье подход — один из возможных [1, 2], позволяющих корректным образом распределить задачи обработки и компенсировать недостатки нижележащих технологий. На наш подход оказал серьезное влияние шаблон MVC.
Идеи MVC
MVC появился в системе Smalltalk-80 для поддержки многоуровневого подхода при создании графических пользовательских интерфейсов [3]. Он был создан в конце 70-х — начале 80-х годов, задолго до того, как такие интерфейсы получили широкое применение. В MVC определялись три компонента (см. рис. 1a):
- модель (model) поддерживает логику приложений и бизнес-логику;
- представление (view) поддерживает логику презентации;
- контроллер (controller) принимает и интерпретирует сигналы, поступающие с клавиатуры и мыши.
Рис. 1. Model-View-Controller |
Цель состояла в том, чтобы отделить код модели (т.е. не связанный с GUI) от презентации. Код модели не содержит никакой информации, касающейся интерфейса, но он рассылает уведомления о любых изменениях состояния соответствующим компонентам (как правило, представлениям). Примерно также происходит во многих существующих компонентных моделях GUI, из которых, пожалуй, наиболее известна JavaBeans.
Эта схема позволяет корректным образом разделить все три уровня, но не лишена недостатков. Во-первых, она поддерживает упрощенное представление о модели и не учитывает никакие различия между логикой приложения (скажем, поток управления и координация разных виджетов) и бизнес-логикой (например, оформление покупки). Во-вторых, в большинстве библиотек GUI и оконных систем в виджетах функции представления и управления объединяются, в силу чего их логическое разделение в представлении и контроллере становится практически бессмысленным. В последних версиях Smalltalk в виджетах операционной системы отказались от использования отдельного контроллера. Во всех разновидностях Smalltalk появился дополнительный уровень для поддержки логики приложения, отделенной от бизнес-объектов. Возможно, самым элегантным из них является механизм Presenter в том виде, как он используется, к примеру, в Taligent и Dolphin Smalltalk [4, 5].
Все это изменило исходную концепцию, в силу чего «контроллером» теперь называют объект, поддерживающий логику приложения, а термин «модель» зарезервирован за бизнес-объектами (см. рис. 1b). Чтобы различать эти две интерпретации MVC, мы используем понятие «модель» для обозначения бизнес-объектов, а «контроллер ввода» и «контроллер приложения» — для двух типов контроллеров.
MVC для Web
Чтобы применить MVC к Web-разработке, мы используем комбинацию скриптов, серверных страниц и обычных объектов, реализуя с их помощью различные компоненты платформы, которую мы назвали Web Actions. В данном контексте применимы обе разновидности MVC. При работе с HTTP ввод и презентация полностью разделены, так что в этом случае отделенный от представления контроллер ввода оказывается удобен. Для приложений произвольной сложности контроллер приложения также необходим, чтобы отделить детали потока приложения от бизнес-логики. На рис. 2 показана базовая объектная структура платформы.
Рис. 2. Платформа Web Actions |
Контроллер ввода. Мы реализовали контроллер ввода в виде скрипта. Одна из важных характеристик описываемой платформы заключается в наличии единого контроллера ввода для всех страниц в Web-приложении; он выполняет синтаксический разбор входной информации, определяет механизм передачи параметров, извлекает все необходимые данные из запроса, во взаимодействии с контроллером приложения определяет следующее действие и инициирует это действие в нужном контексте.
Ограничившись одним скриптом в качестве контроллера ввода, мы локализовали все знания об HTTP и о соглашениях о наименовании на уровне запроса, сократив тем самым дублирование кода, а также упростили последующую модификацию процедур обработки входной информации, поскольку изменения в данном случае надо вносить лишь в одном месте.
Заметим, что класс контроллеров ввода показан как абстрактный класс с одной реализацией для доступа к приложениям по HTTP с помощью обычного браузера и другой реализацией для доступа к приложениям с помощью WAP-устройства.
Контроллер приложения. Мы реализовали контроллер приложения как обычный объект, а не как скрипт или серверную страницу. Он координирует логику, связанную с потоком приложения, обрабатывает ошибки, поддерживает долговременное состояние (в том числе ссылки на бизнес-объекты) и определяет, какое представление отображать. Мы храним контроллер приложений в сеансе, используя ключ, известный контроллеру ввода. Для этого используется соглашение о наименовании со всеми его недостатками, описанными выше, но поскольку это единственная вещь, которая сохраняется непосредственно в сеансе, ее негативное влияние сведено к минимуму.
Один контроллер приложения работает с несколькими Web-страницами. В простом приложении он может отвечать за все страницы; в сложном приложении существует несколько контроллеров приложения для различных областей применения.
Используя один инкапсулирующий всю сохраняемую информацию объект в качестве главной точки обращения для нее, контроллер приложения эффективно решает вопросы упрятывания информации и соглашений о наименовании. Вместо того, чтобы хранить изолированные фрагменты информации в сеансе, мы храним их в бизнес-объектах, обращаясь к ним посредством сообщений из контроллера приложения. Механизмы языков программирования позволяют отслеживать использование контроллера приложения и бизнес-объектов, что упрощает изменение кода. Если используется язык со статической типизацией, то проверка типов служит дополнительной гарантией корректности использования данных.
Действие. Единый контроллер ввода инициирует одно из многих возможных действий для каждого запроса. Одна из его задач состоит в том, чтобы определить, какое именно действие следует выполнить. Это зависит и от данных, вводимых пользователем, и от текущего состояния приложения, поэтому нужное действие определяется совместно с контроллером приложения. Результаты этого определения оформляются в виде объекта Action (реализация шаблона Command [6]).
Бизнес-объекты. Мы реализовали бизнес-объекты в виде обычных объектов, которые содержат только бизнес-логику. Это значительно упрощает разработку и тестирование бизнес-логики в изоляции от инфраструктуры Web. Поскольку бизнес-объекты можно изолировать, необходима возможность использовать одну и ту же реализацию как для тонкого клиента, так и для более широкого диапазона клиентов или даже для традиционного графического пользовательского интерфейса.
Представление. Мы реализовали представления как серверные страницы, которые могут обращаться к контроллеру приложений и бизнес-объектам. Представления должны содержать минимум кода, делегируя большую часть функциональности контроллеру приложений или бизнес-объектам. На странице должен использоваться только код, непосредственно связанный с презентацией на ней. Если поддерживается механизм тегов, то предпочтение отдается пользовательским тегам JavaServer Pages, что позволяет изъять избыточный код.
На рис. 2 показаны два различных механизма представлений. Первый из них использует реализацию серверных страниц, подходящую для Web-браузера или WAP-устройства. Второй генерирует ту же самую информацию в формате XML, который может быть передан апплету, элементу управления ActiveX или приложению GUI.
Web Actions: поток управления
За счет описанной организации скриптов, серверных страниц и обычных объектов удалось решить многие из вопросов, связанных с простой Web-разработкой. Мы минимизировали дублирование кода и зависимость от деталей протокола или от соглашений о наименовании, использовав единый скрипт ввода. Мы добились хорошего упрятывания информации за счет хранения данных в объектах. Мы ограничили использование серверных страниц уровнем представления, одновременно максимально увеличили объем кода, которым мы можем управлять и который мы можем отлаживать с помощью стандартных инструментов и методов программирования. За счет изоляции уровней друг от друга их можно тестировать порознь.
Чтобы представить себе механизм работы более детально, рассмотрим поток управления для Web-запроса в интерактивном банковском приложении. Проанализируем, как контроллер ввода принимает входные данные, выбирает действие и готовится к его выполнению, выполняет соответствующий код контроллера приложения, а затем передает управление представлению (на рис. 3 приведен соответствующий пример кода).
Рис. 3. Код для сервлета контроллера ввода |
Поиск контроллера
Контроллер ввода должен определить, какой контроллер приложения отвечает за текущий запрос. Активные контроллеры приложения хранятся в сеансе. Предполагается, что мы в состоянии определить контроллер, используя поиск, основанный на анализе пути запроса. Скажем, в нашем банковском приложении может иметься контроллер приложений обработки счетов, который используется для любой страницы, связанной с обслуживанием счета.
Получение входных данных
После того как определен необходимый контроллер приложений, нужно извлечь соответствующую информацию из запроса. Главное, надо найти все входные параметры. Они могут быть закодированы как часть URL, как перечисленные после URL параметры запроса или как данные формы. В любом случае входная информация состоит только из цепочек символов, которые должны преобразовываться в параметры соответствующего типа; необходимо проверить их корректность. Контроллер ввода извлекает информацию и вместе с контроллером приложений выполняет базовую синтаксическую проверку, сообщая ему значения параметров. Например, пользовательский запрос для передачи денег можно сформулировать так:
https://objectbank.com/InputController/transfer?from=123&to=321&amt=$50.00
Часть /transfer указывает действие для контроллера ввода. Остальная часть строки содержит параметры запроса: два номера счета и переводимая сумма.
С другой стороны, запрос на изменение данных владельца счета может быть получен из HTML-формы:
https://objectbank.com/InputController/ updateAccountData
Выбор действия
Контроллер приложения может отслеживать приемлемые последовательности операций и предыдущие шаги пользователя. Основываясь на этих наблюдениях и на переданной в запросе информации, можно определить, какое действие предпринять и корректно ли оно в текущем контексте. Это весьма важно, ведь в Web пользователи всегда могут нажать кнопку Back, чтобы отказаться от последовательности операций; в традиционном же графическом пользовательском интерфейсе это невозможно.
В простейшем варианте мы можем определить действия для входа в систему, выхода из нее, перевода средств между счетами, обновления счета и оплаты чеков. Любая деятельность, связанная со счетами, разрешается только после регистрации пользователя в системе, а действия, связанные с переводом денег, имеют еще страницу подтверждения операции. Пользователю предлагается подтвердить запрос, только если указанная непосредственно перед этим операция была запросом. Также выявляется ситуация, когда пользователь отказывается от запроса, повторяет тот же самый запрос или повторно нажимает кнопку Submit, не дожидаясь обработки первого подтверждения.
Выполнение действия
Могут быть выбраны сложные действия, реализованные как объекты, наследуемые из класса Action и выполняющие метод переключения (trigger).
public void trigger(Controller
controller){
BankController ctrlr=
BankController)controller;
Account acct=ctrlr.readAccount
(context.getAccountNo());
ctrlr.setCurrentAccount(account);
ctrlr.setNextPage(«account
Summary «);
}
Это позволяет разделить различные действия, но не обеспечивает тот уровень инкапсуляции, который был бы нужен в контроллере приложения. Вместо стандартного объектно-ориентированного подхода, предусматривающего использование нескольких методов в одном объекте, мы поддерживаем множество объектов-действий, которые меняют состояние контроллера, поддерживая минимум инкапсуляции.
В качестве альтернативного решения можно представить действие просто как индикатор выполняемого метода контроллера приложения. Это очень легко сделать в Smalltalk. Благодаря сервлету Smalltalk и оболочке JSP [7] мы можем просто установить действие
action := #deposit.
а затем его выполнить
controller perform:action.
В этом случае действие — просто имя соответствующего метода; инициируется оно с помощью стандартного метода perform — не нужны ни классы действий, ни операция переключения.
Хранение кода и данных вместе в контроллере приложений обеспечивает лучшую инкапсуляцию, но способно породить проблемы при работе в коллективе, когда много людей одновременно обращаются к одному и тому же объекту.
Передача запроса
Передача запроса помогает разделить обязанности между разными компонентами. Как только действие выполнено, мы определяем URL следующей страницы, которая будет отображена, и передаем запрос. В простой системе действия могут прямо определять следующую страницу, в более сложной системе контроллер приложения может координировать ее выбор, управляя внутренним состоянием.
Вариации
Но возможно и множество других вариантов реализации.
Сложные представления
Мы описали представления как отображение непосредственно на серверные страницы, но в более сложных приложениях это оказывается не столь банальным делом. Так, мы можем создавать составные представления из нескольких простых. В частности, это может быть домашняя страница с подразделами в виде фреймов или с подразделами, определенными путем включения результатов других Web-запросов. Такую структуру можно моделировать либо напрямую, генерируя новые запросы, либо используя внутренний механизм явной передачи запроса на другую страницу.
Кроме того, иногда есть нужда поместить на уровень представления объекты другого слоя. Например, при обработке множества возможных форм вывода может потребоваться объект View, который реализует особенности вывода на конкретное устройство. К примеру, соответствующее направление вывода может задавать особый ключ. Его можно передать серверным страницам для презентации в вариантах WAP и HTML, а также экрану для GUI. Использование объекта View помогает изолировать презентацию результатов действия от самого действия. Аналогичным образом можно реализовать поддержку выходной информации на различных языках.
Контекст действия
В некоторых ситуациях может оказаться полезным отделить часть состояния от контроллера. В частности, если простое отображение из URL на контроллер приложения не поддерживается, для определения корректного контроллера может потребоваться получить более подробную информацию о состоянии запроса. Сделать это можно посредством объекта ActionContext, который хранит состояние запроса в стандартной форме. В этом случае создается контекст, используемый для поиска соответствующего контроллера, а затем действия применяются к сочетанию контроллера и контекста.
Бизнес-логика и компоненты
Мы не стали подробно останавливаться на уровне бизнес-логики, поскольку, с нашей точки зрения, это обычная объектно-ориентированная программа. Однако она может быть достаточно сложной и использовать ряд других технологий. Одна из наиболее часто встречающихся — взаимодействие между данным уровнем и компонентами, скажем, Enterprise JavaBeans. Некоторые Java-платформы используют EJB в качестве стандартного механизма для уровня бизнес-объектов.
Нельзя сказать, что это решение всегда оказывается наилучшим. На уровне бизнес-логики могут найтись убедительные причины для обращения к компонентам (в первую очередь, «сеансовые» компоненты, инкапсулирующие доступ к системам поддержки транзакций или к унаследованным системам), однако непосредственное использование компонентов для представления бизнес-логики кажется неразумным. Для компонентов свойственна некоторая степень автоматизации таких аспектов, как транзакции, защита и постоянное хранение, однако они накладывают серьезные ограничения на архитектуру и значительно снижают производительность даже относительно производительности Java. В частности, обременительным оказывается отсутствие наследования. Существуют другие платформы для работы с транзакциями, защитой и производительностью в контексте обычных бизнес-объектов. Наконец, тестировать EJB изолированно трудно, ведь для их работы требуется контейнер; это значительно усложняет разработку. Использование сеансовых компонентов для оформления бизнес-объектов (или вообще отказ от EJB) представляется разумным решением.
Описанная платформа позволяет разработчикам в первую очередь сосредоточиться на написании кода приложения, а не на работе с сервлетами, запросами или переменными сеанса. Мы используем ее в самых разных приложениях, работая и со Smalltalk, и с Java, добиваясь при этом более высокого качества программного обеспечения, поскольку принципы объектно-ориентированного программирования в этом случае не воспринимаются как догмы, а естественным образом следуют из структуры платформы. Как это не парадоксально, но она помогает программистам, не имеющим большого опыта работы с объектно-ориентированными методами, создавать качественный код, даже не зная платформы во всех подробностях.
Литература
[1] M. Ellis, N. Dai, «Best Practices for Developing Web Applications Using Java Servlets». OOPSLA 2000 tutorial; www.smalltakchronicles.net/papers/Practices.pdf
[2] N. Dai, A. Knight, «Objects versus the Web». OOPSLA 2001 tutorial; www.smalltalkchronicles.net/papers/objectsXweb10152001.pdf
[3] E. Gamma et al., Design Patterns, Addison-Wesley, Reading, Mass., 1994
[4] G.E. Krasner, S.T. Pope, «A Description of the Model-View-Controller User Interface Paradigm in the Smalltalk-80 System», J. Object-Oriented Programming, vol. 1, no. 3, Aug. 1988
[5] M. Potel, MVP: Model-Viewer-Presenter, tech. report, IBM, 1996; www-106.ibm.com/developerworks/library/mvp.html
[6] A. Bower, B. MacGlashan, «Model-View-Presenter Framework», 2001, www.object-arts.com/Education-Centre/Overviews/ModelViewPresenter.htm
[7] VisualWave Application Developer?s Guide, tech. report, Cincom Systems, 2001
Алан Найт (knight@acm.org) — ведущий разработчик компании Cincom Systems, где он занят развитием Web-инструментария Smalltalk. К области его научных интересов относятся Web-разработка, взаимосвязи объектной и реляционной моделей, системы групповой разработки программ.
Нейси Дай (nacidai@acm.org) — независимый разработчик и педагог. Преподает объектные технологии, Java, методы проектирования и распределенные вычисления; руководит проектами, связанными с Web-разработкой. Интересуется проблемами прикладной инженерии и вычислительной физики.
Alan Knight, Naci Dai, Objects and a Web. IEEE Software, March/April 2002. IEEE Computer Society, 2002, All rights reserved. Reprinted with permission.
Определения
Большинство из описанных приемов применимо к любой технологии в соответствующих категориях, но там, где важны детали, в качестве примера технологий мы используем сервлеты и Java Server Pages. Номинально и то, и другое связано с Java, однако соответствующие идеи без труда можно применить к другим языкам и средам разработки, в частности, к Smalltalk и C++.
Многоуровневая архитектура
Многоуровневая архитектура — это система, содержащая несколько четко отделенных друг от друга уровней с минимальными зависимостями и взаимодействиями между уровнями. Такая система обеспечивает хорошее разделение функций, которое позволяет работать с различными фрагментами кода независимо, не затрагивая другие уровни или затрагивая их в минимальной степени. Выделение независимых фрагментов системы увеличивает ее адаптируемость и облегчает модификацию по мере изменения требований. Мы описываем уровни ввода, логики приложений, бизнес-логики и презентации.
Ввод
Уровень ввода содержит код, выполняющий «прием» и проверку синтаксической корректности входных данных. В контексте Web эта функциональность включает в себя синтаксический разбор входного потока HTTP и извлечение параметров из HTTP-запросов. В случае Model-View-Controller это соответствует контроллеру ввода (input controller).
Логика приложений
Код логики приложений обрабатывает общий поток данных приложения. Его часто называют связующим уровнем, отделяющим бизнес-логику от входных данных и презентационной логики и управляющим их взаимодействием. Все это требует определенного знания обоих уровней. Так, этот уровень вовлечен в преобразование данных презентационного уровня в цепочки символов и в работу с соответствующими сообщениями или состояниями бизнес-объектов, равно как управляет многостраничным Web-взаимодействием как последовательностью шагов. В Model-View-Controller соответствует контроллеру приложений (application controller).
Бизнес-логика
Код бизнес-логики отвечает исключительно за базовые бизнес-функции. Он должен быть полностью независим от уровня презентации. Бизнес-логику реализуют так называемые бизнес-объекты. В сложном приложении бизнес-логика, скорее всего, будет самым большим компонентом и может включать в себя код, который обращается к другим системам (внешние базы данных, компоненты, различные службы и др.). В Model-View-Controller соответствует модели (model).
Презентация
Данный уровень содержит код и другие ресурсы (скажем изображения и HTML-текст), используемые для внешнего представления приложения. Как правило, содержит незначительный объем кода — только то, что связано с форматированием и представлением данных. В контексте Web примером такого кода могут служить фрагменты кода, которые выводят значения на динамически генерируемой странице Web. В Model-View-Controller соответствует представлению (view).
Скрипты
Многие базовые Web-технологии можно сгруппировать в так называемую категорию скриптов — небольших программ, которые выполняют обработку HTTP. В эту категорию входят, в частности, скомпилированные программы, файлы с кодом на языке скриптов (Perl, Python, Ruby, VBScript и др.) и сервлеты Java. Эти технологии порядком отличаются друг от друга, однако все они имеют одно фундаментальное свойство — подобные программы получают HTTP-запрос и посылают обратно ответ. В простейшем случае, выполняясь, скрипт не сохраняет состояния и не зависит от всех других скриптов. Важным свойством является способность/неспособность скрипта совместно с другими скриптами использовать память; примерами могут служить сервлеты и CGI-программы соответственно. Общая память позволяет скриптам более эффективно расходовать отведенные им системные ресурсы за счет их объединения в пул. Кроме того, модель программирования упрощается — за счет сохранения состояния, но и ценой более сложной инфраструктуры.
Серверные страницы
Альтернативный режим поддержки скриптов предусматривает снабжение HTML-страниц «аннотациями» в виде небольших фрагментов кода. Многие языки скриптов в дополнение к «чистым» скриптам поддерживают такого рода возможность; яркими примерами тому являются JavaServer Pages (JSP), Microsoft Active Server Pages, PHP и Zope. Существуют также вариации данного подхода, при которых страницы аннотируются особыми тегами HTML или XML, указывающими, какой код следует исполнять, встретив эти теги. Примерами могут служить JSP beans, пользовательские теги, а также XMLC компании Enhydra.
Похожий подход
Существует множество платформ и методов проектирования, созданных под влиянием многоуровневого подхода, Model-View-Controller и объектной парадигмы. Одновременное использование сервлетов и серверных страниц, а также максимальное изъятие кода с серверных страниц в кругах Java-разработчиков называют «второй моделью Web-программирования» (model 2 Web programming). Самая известная платформа, следующая этим принципам, — свободно распространяемая среда Jakarta Struts (jakarta.apache.org/struts).
Struts — это платформа контроллера для Java, используемая для создания на базе JavaServer Pages представлений, которые связываются с бизнес-моделями посредством единого сервлета контроллера. Struts очень близка ко многим нашим концепциям и тоже использует один сервлет контроллера ввода, но в ряде важных аспектов отличается. Самое главное: в ней не выделяется отдельный контроллер приложений, а есть только ввод, действие, презентация и модель.
Функции контроллера приложений назначаются действиями, объектами моделями или декларативными аспектами платформы. Действия — это объекты-команды, и в Struts предполагается, что они должны делегировать модели максимум поведенческих аспектов. Однако приведенные примеры кодирования свидетельствуют, что в некоторых действиях может содержаться управление приложениями или моделирование поведения. Скажем, переход на следующую страницу после действия может быть указан путем редактирования файлов конфигурации. Еще одно отличие состоит в том, что в Struts объект модели может определяться как форма bean form, имеющая только состояние, или как более обычный объект, обладающий и функциями, и поведением. Механизм bean form используется для хранения данных, необходимых для обработки презентации или ввода. У нас аналогичного механизма нет и неясно, каким образом представить объекты, имеющие только состояние.