Разработка механизма для объектного построения web-приложений и распределенных информационных систем

Оглавление

Постановка Задачи

На текущий момент для построения web-приложений используются различные технологии и подходы. Создавая новый сайт, разрабатывая информационную систему, команда разработчиков часто сталкивается с проблемой – какую технологию выбрать для решения поставленной задачи. Предпочтения обычно отдаются знакомым технологиям, или же диктуются рамками поставленной задачи. Но независимо от выбранной технологии, каждый раз при создании нового web-сайта, зачастую приходится проходить множество одних и тех же этапов разработки: Заметим, что часть таких задач можно решить уже сразу, выбрав нужную технологию и среду разработки. Например, подходящей и удобной платформой для создания порталов можно считать продукт WebSphere от компании IBM. Для обычного сайта средней сложности можно использовать среду разработки Visual Studio .NET и множество уже готовых «контролов» для ASP.NET. Но кроме одиночной разработки, часто приходится встречаться с разработкой множества сайтов, а иногда и с параллельной разработкой системы сайтов. Перед програмистом встает задача переиспользования уже написанного один раз кода. Можно ориентироваться на классический метод "copy & paste", который увеличивает сложность разработки и дальнейшей поддержки. Можно использовать готовые компонентные архитектуры (например, портлеты для WebSphere, контролы ASP.NET), но при этом потенциально теряя гибкость разрабатываемой системы (например – инкапсулированность html кода в ASP.NET контролах диктует разработчику определенное представление дизайна для разрабатываемого сайта и к тому же может привести к несовместмости конечного варианта генерируемого html с различными браузерами). Для решения перечисленных задач была разработана, описанная далее, гибкая трехуровневая компонентная архитертура на основе технологий Java/JSP (Resin/Tomcat) и XML/XSLT.

Модельная реализация системы объектного построения сайтов.

В любом Web-приложении выбор технологии зависит от природы приложения, организации и даже предпочтений команды разработчиков. На сервере можно использовать множество технологий и подходов. Многие из них можно применять совместно, но независимо от выбранной технологии её необходимо выразить в модели всей системы.

1.1  Типы объектов используемых в модели

Определим терминологию: компонент в нашей модели – это объект некоторого класса; принадлежащий пакету (package) или сборке (assembly/namespace), обладающий определенными свойствами и реализующий специфичные для пакета интерфейсы. Все рассматриваемые компоненты делятся  на четыре группы:

1)      Объекты PO              (Presentation Object)
2)      Объекты BL              (Business Object)
3)      Объекты Dict             (Dictionary Object)
4)      Объекты DB              (Database Object)

Объекты уровня баз данных (DB) предоставляют общий механизм проведения простых и достаточно сложных операций с выбранной базой (MySQL, Postgress,Oracle и т.д.). В основном - это набор методов для создания SQL запросов и их последующей обработки. Все классы (обычно - объекты бизнес логики) работают с базой только через объекты этого уровня. Создание DB объектов заключается в наследовании от некоторых (одного из некоторых) абстрактных классов и описании дополнительных методов, ранее нереализованных в абстрактном предке, и специфичных для конкретной структуры таблиц. Объекты уровня BL отвечают за логику работы отдельно взятого приложения и в общем случае могут иметь произвольную структуру. Они описывают модель поведения приложения и предоставляют собой обработчики данных поступающих из PO объектов.

1.2  Архитектура системы

Для начала, остановимся подробнее на описании объектов презентационной логики, как основной архитектурной единицы системы. Объекты уровня PO реализуют собой интерфейс, обеспечивающий XML представление приложения, и отвечают за общение с браузером. По своей сути, PO объекты - это некоторый набор классов описывающих вид отдельных страниц, они наследуются от готового набора абстрактных и представляют в себе набор определенных методов отвечающих за генерацию отдельных частей интернет странички. То есть все страницы сайта (каждая из них) разбиваются на наиболее общие для всех них части (блоки навигаций, реклама, центральная часть, верхняя и нижняя).  PO объект может сгенерировать ХМЛ для всех частей странички, за которую он отвечает (для каждой части свой метод). Либо если часть методов им не реализована, то нереализованные этим PO объектом методы вызываются у предыдущего вызванного PO объекта (и так по цепочке далее). 

Пример: Заходим на сайт. Вызывается PO объект, который рисует главную страничку (все её части). Далее идем по какой-либо гиперссылке. И если, например, отличие соответствующей ей страницы сайта лишь в заполнении центральной части странички, то и PO объекту, отвечающему за генерацию XML для неё, достаточно будет реализовать метод рисующий XML именно для центральной части... и т.д.

Обработка запросов к системе

Основной идеей создания PO объектов было то, что пользователь системы в каждый определенный момент времени может быть в какой-либо роли, например:
Соответственно, находясь в каждой из ролей, пользователь может и должен иметь доступ к возможностям предыдущей роли. Например, когда человек  попадает на первую страницу интернет сайта, то он определяется как простой пользователь. После того как он введет свое имя и пароль, то он сохраняет все свойства пользователя, но также получает свойства зарегистрированного пользователя (рис.1). Надо заметить, что стрелки наследования на данной диаграмме не означают наследование соответствующих классов. Это расширение того, что может делать пользователь. Программно  данная модель реализована в виде стека объектов. То есть - это в некотором роде иерархия параллельная наследованию. Она не исключает использование наследования.

Пример: Так, если часть сайта «Пользователь»/«Раздел сайта B» сходна с «Пользователь»/«Авторизованный пользователь»/«Раздел сайта B», то имеет смысл унаследовать PO объект (класс, отвечающий за эту часть сайта) «Пользователь»/«Авторизованный пользователь»/«Раздел сайта B» от PO объекта «Пользователь»/«Раздел сайта B»

Рис.1 (схема доступа к различным разделам интернет сайта, зависящая от ролей пользователя)

Все гиперссылки используемые на сайте делятся на две группы – внешние и внутренние. Внешние гиперссылки – они имеют произвольный формат и ведут за пределы интернет сайта. Внутренние же – служат для построения навигации по сайту. Вся необходимая информация для обработки запроса кодируется в URL.

Использование иерархии объектов в обработке запроса

Как уже было сказано ранее, вся необходимая для обработки запроса информация размещается в адресе интернет страницы (URL), например:
В данном примере закодирована последовательность имен объектов,  которые должны быть помещены в стек (или уже находятся в стеке) и которые могут правильно обработать параметры, переданные через URL. Каждый из объектов в стеке " Пользователь/Авторизованный пользователь/Раздел сайта A " умеет рисовать, необходимы для него области. Например, класс " Пользователь " знает, как нарисовать все области. Класс " Авторизованный пользователь " уже не знает, как рисовать область навигации, поскольку она едина в пределах приложения. Он делегирует это классу " Пользователь ". Класс " Раздел сайта A " может уже не знать про часть страницы ответственную за авторизацию пользователя, и про область навигации.

Программно это реализовано, как стек объектов. То есть для ссылки  " Пользователь/Авторизованный пользователь/Раздел сайта A " стек представляет собой " Раздел сайта A/Авторизованный пользователь/ Пользователь ". Основной модуль приложения, при генерации XML для страницы, пробегает эти объекты именно в таком порядке. По этому, если какой-то объект вверху стека вернул XML для какой-то области, то ниже лежащие объекты уже не опрашиваются.

Рис.2 (стек объектов)



Кроме непосредственно генерации страниц необходимо решить и ряд других задач. К самым главным из них относится создания и инициализация объектов в стеке, а также обработка данных посланных пользователем системы после заполнения форм.

Инициализация объектов

В начале рассмотрим вопрос создания и инициализации стека объектов. По самому запросу ясно, какие объекты нам надо создавать, но этого мало, поскольку многие объекты требуют для своей работы различных инициализационных параметров. Так объекту класса «Авторизованный пользователь» нужно знать имя пользователя, для которого он создан. Как же это сделать? Был предложен следующий механизм. В начале обработки каждого запроса происходить цикл идентификации. В данном цикле объектам в стеке дается возможность прочитать необходимые данные из запроса. Причем в данном случае стек пробегается в обратном порядке, то есть для ссылки
" Пользователь/Авторизованный пользователь/Раздел сайта A " порядок идентификации есть «Пользователь», «Авторизованный пользователь», «Раздел сайта A». Если какой-то объект в данной цепочке обнаруживает, что он не может инициализироваться на основе имеющихся данных, то происходит особая обработка данного HTTP запроса.
Рассмотрим ситуацию, когда объект «Авторизованный пользователь» создается в первый раз. Тогда он не может получить информацию о пользователе откуда-либо кроме как запроса. Если ее там нет, то данный объект вернет как результат метода identify false. После этого дальнейший цикл идентификации прерывается, т.е. метод  identify для объекта «Раздел сайта A» не будет вызван, и начинается цикл генерации страницы. При этом, хотя сгенерированная страница и имеет ссылку вида
" Пользователь/Авторизованный пользователь/Раздел сайта A ", для ее генерации используется только те объекты, до которых дошел цикл идентификации. То есть, вместо стека " Раздел сайта A/Авторизованный пользователь/Пользователь " используется «Авторизованный пользователь/Пользователь». При этом, поскольку объект «Авторизованный пользователь» знает про то, что он не смог получить необходимые данные, то он сгенерирует форму ввода для тех данных, что ему нужны. В данном случае это будет форма для выполнения входа в систему для зарегистрированного пользователя.  После ее заполнение и отправки на сервер объект класса «Авторизованный пользователь» сможет получить всю необходимую информацию и пропустить цикл идентификации до следующего объекта.

Механизм поддержки обработки форм

В простейших случаях для обработки данных приходящих из HTML форм может оказаться достаточно механизма идентификации, но по большому счету он все-таки предназначен для других целей.  Часто после заполнение какой-либо формы меняется стек объектов. Поэтому для поддержки обработки форм в систему был добавлен еще один этап обработки запроса. Если запрос содержит два параметра source (источник) и event (событие), то между циклом идентификации и генерации страницы может произойти следующие: в стеке  будет найден объект с именем класса из источника и у него будет вызван метод имя, которого определяется из события. В этот метод будет переданы все данные, которые пришли от пользователя. После завершение обработки запроса данный объект вернет в качестве результата массив объектов, которые могут быть добавлены в стек. Реально возможны следующие ситуации:
  1. Результатом будет пустая строка. Это интерпретируется системой, как удаления из стека этого объекта и всех объектов, что находятся в ссылки за ним. Например, таким образом, работает операция выхода из системы для зарегистрированного пользователя. Объект «Авторизованный пользователь» просто имеет метод logout, который возвращает пустую сроку.
  1. Результатом может быть массив из одного объекта, того самого, который вернул этот массив. Данная ситуация имеет место, когда пользователь допустим заполнил не все обязательные поля в форме ввода или ввел не корректную информацию. Тогда эта форма будет вновь выведена на экран, с необходимыми сообщениями об ошибках пользователя.
  1. Будет возращен массив из объектов, которые необходимо добавить в стек. Эти объекты будут добавлены в систему.
Несмотря на то, что данная система не обладает возможностью произвольной модификации стека объектов, она обеспечивает необходимую гибкость в управление его состоянием. А произвольное изменение стека любым объектом противоречит принципам объектно-ориентированного программирования, поскольку предполагает слишком глубокую связь между объектами различного уровня.

Использование Soap-RPC для удаленного использования объектов

Для каждого PO объекта, зарегистрированного в системе, хранится описание в конфигурационном файле. В это описание входят: Когда в процессе работы системы, ей нужно, ей нужно использовать методы какого-либо объекта, она, прежде всего, читает описание объекта из файла конфигурации. Для вызова методов удаленных объектов используется механизм, предоставляемый протоколом SOAP. SOAP - это простой протокол доступа к объектам (Simple Object Access Protocol).

Удаленные объекты проходят те же стадии идентификации,  что и расположенные локально. Для правильной работы системы, необходимым условием было лишь использование на удаленной машине архитектуры аналогичной предложенной в данной работе.

Если для ссылки " Пользователь/Авторизованный пользователь/Раздел сайта A/ Таблица с данными ", объект «Таблица с данными» расположен на другом сервере, то система, пользуясь конфигурационными данными, удаленно вызывает необходимые методы этого объекта, передавая им все параметры, и в конечном итоге получает XML, представляющий собой те данные, которые требуется выдать по запросу на браузер пользователя.  

1.3 Описание компонент

Работа с базами данных.

Современное динамическое приложение трудно представить без использования базы данных. В связи  с этим остро стоят вопросы работы с базой данных. Одной из основных задач является взаимное соответствие между объектами в программе и данными в базе данных. Главная трудность здесь это различные подходы. Если в базах данных основным является реляционная модель данных, то с другой стороны на уровни web сервера работа ведется с объектами. Существует несколько способов соответствия между объектами и классами с одной  стороны и таблицами базы данных с другой стороны. Надо заметить, что существуют объектно-реляционные базы данных, работа с которыми описана, например в [3], и так же уже существующие реляционные базы (RDBMS) в последние время получают объектные расширения. Но при использовании данных возможностей конкретной базы данных неизбежно теряется универсальность решения. Дело в том, что в отличие от стандартизованного языка SQL для работы c RDBMS, эти возможности только находятся на этапе стандартизации и широкого внедрения. Многие базы данных их не поддерживают вообще. Использование объектов на уровне базы данных может привести и к другим неприятным последствиям. К ним можно отнести: Рассмотрим непосредственно работу с произвольной базой данных в предложенной модели системы (рис.3 и рис.4). Конечным элементом диаграммы на рисунке является некий абстрактный класс, который может быть реализован практически на любом языке программирования поддерживающем ООП.

Рис.3 (первый уровень абстракции работы с базой данных)

На рисунке 3 представлена диаграмма классов, которая позволяет избежать прямой работы с SQL выражениями в исходном коде. Кроме того, благодаря таким классам оказывается возможным контролировать исполнение запросов к базе данных, поскольку все они проходят через эти классы. Следующий уровень работы с базой данных – это установление соответствия между строками базы данных, получаемыми через экземпляры классов первого уровня, и объектами системы.     В данном случае используется модель проектирования (design pattern), как композит (Composite) [4]. Данная модель позволяет единообразно работать со своими составными элементами, которыми в данном случае являются  отдельные поля в кортеже. Ниже (рис.4) представлена иерархия для контейнеров, в которые помещается композит. Надо заметить также, что эти контейнеры  могут также содержат информацию с метаданными. То есть в них описаны атрибуты таблицы, их тип, при необходимости и другие их свойства, например, такие как максимальная длина данных, обязательность поля. Благодаря этому вся информация о схеме базы данных заключена в строго ограниченном наборе классов,  что позволяет легко ее корректировать в случае необходимости. Обычно такая корректировка актуальна в момент разработки, поскольку часто происходит изменение и добавление новых требований к системе.

Рис.4 (второй уровень абстракции работы с базой данных)



Каждый тип в базе данных соответствует  некоторому объекту бизнес логики в нашей модели. Здесь речь не идет о стандартном соответствии типов языков SQL и C# / Java / PHP.   Дело в том это слишком низко уровневая процедура, которая не позволяет решить поставленные задачи. Композит содержит хэш-таблицу, где ключом является имя атрибута (колонки таблицы), а значением объект соответствующего типа. Данная модель так же предполагает использование вложенных композитов. Это соответствует наследованию классов предметной области. То есть две таблицы: таблица атрибутов предка, и связанная с ней с помощью внешнего ключа таблица атрибутов потомка. Для подобных схем реализуема поддержка транзакционной целостности, т.е. запись в обе таблицы при сохранении или создание объекта происходит в пределах одной транзакции.

Поддержка работы с базами данных также включает некоторые другие классы не рассмотренные здесь. Это пул соединений к базе данных (connection pool), классы для работы с большими бинарными объектами и другие. Надо заметить, что данная архитектура тесно связана со следующим разделом, а именно с поддержкой представления и модификации информации из базы данных. 

Объекты бизнес логики

Объекты бизнес логики, как было сказано ранее, могут иметь произвольное содержание, которое диктуется логикой, необходимой для реализации отдельно взятого интернет сайта. Но вне зависимости от логики работы любого интернет сайта, для общения с пользователями все же должен существовать унифицированный механизм. В HTML для этого есть специальный набор тегов для создания и обработки форм.

В данной модели построения сайтов работа с формами как с объектами вынесена в отдельный пакет классов называемый Dictionary. Данный пакет реализует основные типы полей для ввода информации, включая такие как списки, поля для ввода дат и другие. Основная идея в реализации базового набора компонент по работе с формами состоит в том, что каждый объект данного пакета имеет своего «двойника» в пакете для базы данных. Но обратное утверждение не совсем верно, поскольку один и тот же класс для объекта базы данных может иметь несколько «двойников». Так, например, атрибут String имеет следующие соответствия: TextField, TextArea, Password. Несмотря на связь этих пакетов Dictionary и DB между собой ими можно пользоваться и по отдельности. Это имеет смысл, например в случае форм поиска. Поскольку в данном случае вводимая информация не предназначена для сохранения в базу данных, то форма поиска будет использовать композит без «привязки» к базе данных.

Рис.5 (диаграмма основных классов пакета Dictionary)



Рис.6 (пример работы с формой ResumeForm в объекте PostResumePO, последовательность действий, при модификации данных и их сохранение в базу данных при заполнении пользователем формы ввода)



Сборка и генерация XML

Рассмотрим теперь принципы построения самих интернет страничек. В пределах каждого сайта расположенного в интернете, должен соблюдаться какой либо единый стиль оформления. То есть актуальным встает вопрос о разделении информации (отображаемых данных) и способа их представления пользователю. Для этой цели хорошо подходят технологии XML/XSL. При использовании предложенного подхода получаем, что достаточно универсально описать документ (страницу сайта) в терминах XML разметки и далее, для получения конечного HTML кода, применить XSL преобразование к полученным данным. Рассмотрим теперь уже структуру XML документа. Для каждого сайта легко выделяются некоторые отдельные самостоятельные зоны, на которые можно поделить все странички (назовем эти зоны блоками) и отдельно обрабатывать каждый из блоков. Наиболее типичными блоками документа являются – HeaderPaner, FooterPanel, NavigationPanel, QuickNavigationPanel, ContentPanel и AdvertisingPanel (см. рис.7)

Рис.7 (пример разделения страниц сайта на структурные блоки)


Header Panel


N
a
v
I
g
a
t
I
o
n
P
a
n
e
l

Quick Navigation Panel                   








Content
Panel
A
d
v
e
r
t
I
s
I
n
g
P
a
n
e
l

Footer Panel


HeaderPanel и FooterPanel – это соответственно верхняя и нижняя части страниц, обычно содержащие логотипы и небольшие наборы ссылок специфичных для сайта (About Us, Feedback и т.д.). NavigationPanel и QuickNavigationPanel – это части страниц, содержащие некоторый набор определенных гиперссылок, предназначенных для навигации по сайту. AdvertisingPanel – это некоторый блок рекламы. И, наконец, ContentPanel – это центральная часть любой странички, содержащая какие либо тексты, отдельные части других документов и т.д. То есть, это та часть, содержимое которой должно быть различным на разных страницах сайта. Описанный ранее базовый класс PO (presentation object) имеет специальный метод render (String ID), который по имени или переданному ID блока выдает результатом XML описывающий нужный блок. По результатам опроса всех PO объектов, вовлеченных в процесс обработки запроса пользователя, составляется полный XML документ, описывающий страницу сайта.

Эксперименты

Разработанная в данной работе модель построения интернет сайтов была опробована при создании нескольких больших информационных систем, в которых использовались различные технологии – Java/JSP (Resin/Tomcat), PHP4 (Apache), ASPX/C# (Microsoft Internet Server). При создании информационной системы на основе платформы Microsoft .NET были использованы специфичные особенности языка C#, а именно - механизм создания атрибутов и более удобный, чем в Java, Reflection API, что позволило немного переработать модель работы с базами данных и встроить в неё автоматическое построение XML образов для экземпляров классов объектов DB. В ходе проведенных экспериментов (при использовании предложенной  модели построения интернет сайтов)  вследствие унификации программного кода, а так же модульного подхода при разработке и построении сайтов, были получены дополнительные результаты:

Заключение

Перечислим по пунктам основные результаты:

Литература

  1. Антон Элиенс. Принципы объектно-ориентированной разработки программ. Издательский дом «Вильямс», Москва, 2001. [стр.357-411]
  2. Эдвард Йордон. Структурные модели в объектно-ориентированном анализе и проектировании. Издательство «Лори», 1999. [стр.147-158]
  3. Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Addison Wesley 1998 [стр.50-64]
  4. Karl Avedal, Richard Browett, Jason Diamond. Professional Java Server Programming J2EE Edition. Wrox Press 2000