В работе рассматриваются некоторые аспекты
построения клиентского приложения на базе WEB броузера. Необходимо
отметить, что информационная система, в основе построения которой
лежат Web-технологии, в определенных ситуациях может не только
сохранить возможности классического приложения, т.е. приложения
разработанного в рамках клиент-серверной технологии, но зачастую и
расширить их. Кроме того, применение Web-технологий в этом классе
задач имеет и свои преимущества. К наиболее важным из них, можно
отнести отсутствие необходимости в установке и сопровождении
клиентских приложений, а также затрат на покупку лицензий для
программного обеспечения, которое бы осуществляло поддержку работы
front-end приложений, что неизменно присутствует в клиент-серверной
технологии.
С другой стороны, размытость стандартов в области
WEB-технологий и некоторая "сыроватость" используемого здесь
программного обеспечения являлись, до последнего времени, фактором
сдерживающим их распространение. Тем не менее, в настоящий момент
можно уже говорить не столько о возможности распространения
WEB-технологий, сколько об успешности распространения таких
технологий на задачи, решаемые, например, в корпоративных сетях.
Поводом к написанию данной статьи послужил
практический опыт построения подобной системы, которым автор хочет
поделиться. В небольшой статье нет возможности рассмотреть весь
спектр проблем и их решений, однако, наиболее существенные моменты
будут затронуты.
Постановка задачи
Необходимо на основе стандартного броузера
реализовать клиентскую часть запросной системы к базе данных.
Пользователю система должна предоставлять:
Одновременное формирование произвольного числа запросов с
использованием объектов HTML-формы;
Автоматическое запоминание и инициализацию критериев текущего
запроса;
Просмотр, корректировку и сброс критериев текущего запроса;
Создание нового запроса, удаление и выполнение текущего
запроса.
Кроме того, клиентская часть системы должна быть
способна к быстрому расширению и модификации, что с точки зрения
разработчика означает, что ее код должна быть по возможности
универсален и открыт.
Здесь мы привели только небольшой ряд требований,
который может быть предъявлен к реальной запросной системе,
ориентируясь на их достаточную распространенность. Однако ничто не
мешает включить сюда, например, требование автоматической
верификации введенной пользователем информации или возможность
изменения логических отношений между критериями запроса. Только
ограничение на объем статьи, а также нежелание автора уходить в
описание пусть интересных, но все-таки второстепенных деталей,
повлияло на исключение их из постановки задачи.
Необходимо также подчеркнуть, что в данной работе
не рассматривается построение серверной части запросной системы. В
этом смысле, вопрос о технологии, которая будет использована на
сервере остается открытым и зависит, по-видимому, от требований
предъявляемых конкретной разработкой. В данной же работе делается
ориентация на обработку введенной информации CGI-скриптом.
Реализация
1. Объекты клиентской части
Язык JavaScript, базируется на
объектно-ориентированной модели программирования, хотя его и нельзя
назвать полноценным объектным языком. Так например, в этом языке
отсутствует понятие класса. Тем не менее, наиболее эффективное
решение указанной задачи видится в применении именно объектных
возможностей JavaScript.
Анализ требований, предъявляемых нашей задачей,
выявляет три базовых объекта, которые необходимо реализовать на
JavaScript:
массив запросов [array_of_query];
запрос [query];
критерий запроса [criterion].
В языке JavaScript не предусмотрены конструкции для
определения сложных типов данных, поэтому пользовательские объекты
конструируются с помощью функций. Ниже приведена реализация
средствами JavaScript конструктора первого в этом списке объекта -
массив запросов [array_of_query]. function array_of_query()
{
// Свойства
this.array_of_query = new Array() // Вводит массив объектов (запросов)
this.id = -1 // Определяет уникальный идентификатор запроса
// Методы
this.array_size = array_size // Возвращает кол-во запросов в массиве
this.add_query = add_query // Добавляет запрос в массив запросов
this.del_query = del_query // Удаляет запрос из массива запросов
this.index_query = index_query // Возвращает индекс запроса в массиве запросов
}
Как видно, в данном конструкторе определены 2
свойства (переменные) и 4 метода. Конструирование конкретного
объекта (или в терминологии объектно-ориентированного
программирования - определение экземпляра класса) осуществляется в
JavaScript следующим способом: имя_объекта = new имя_функции_конструктора()
Для пояснения отдельных моментов, нам потребуется
конкретный экземпляр такого объекта, поэтому сейчас полезно его
ввести: stack = new array_of_query()
Теперь самое время сказать несколько слов о
реализации методов. Методы в JavaScript вводятся как обычные
функции, причем из-за невозможности полной инкапсуляции метода в
конкретный класс, наименование функционально подобных методов для
различных конструкторов-классов должно быть различным. Ниже приведен
код метода вышеприведенного объекта, который добавляет новый запрос
в массив запросов. function add_query(name)
{
this.id = this.id+1
this.array_of_query[this.array_of_query.length] = new query(name, this.id)
return this.id
}
Здесь надо обратить внимание на наличие формального
параметра в заголовке реализации функции, и на его отсутствие при
описании функции в конструкторе объекта, которому принадлежит метод.
Формальным параметром данного метода является наименование нового
запроса, который вместе с генерируемым автоматически уникальным
идентификатором, передается в конструктор, второго важного объекта -
[query]. Приведем и его код: function query(name,id)
{
// Свойства
this.query_name = name // Имя запроса
this.query_id = id // Уникальный идентификатор запроса
this.query = new Array(); // Вводит массив объектов (критериев запроса)
// Методы
this.query_size = query_size // Возвращает количество критериев в запросе
this.ini_criterion = ini_criterion; // Осуществляет явный ввод нового критерия
this.add_criterion = add_criterion; // Добавляет новый критерий из формы
this.del_criterion = del_criterion; // Удаляет критерий
}
Итак, данный конструктор вводит в массив запросов
новый запрос, который характеризуется именем (задаваемым
пользователем и, вообще говоря, не обязательно уникальным) и
уникальным идентификатором запроса, который генирируется
автоматически методом объекта [array_of_query]. Необходимо отметить,
что объекты [query] нельзя вводить явным способом., и единственный
корректный способ ввода нового запроса - использование метода
add_query.
Таким образом, после осуществления всех описанных
выше шагов, в массив запросов всегда можно ввести запрос, который
будет иметь имя, уникальный идентификатор, и включать в себя, на
этом этапе, нулевое количество критериев запроса.
Перед рассмотрением третьего, наиболее важного
объекта клиентской части - критерий запроса [criterion], необходимо
дать дополнительные пояснения. Дело в том, что критерий запроса, как
мы увидим, характеризуется сравнительно большим количеством свойств.
Более того, конструктор этого объекта, в отличии от двух предыдущих,
не может быть достаточно универсальным, и имеет привязку к
требованиям, предъявляемым конкретной информационной системой.
С другой стороны, все свойства критерия запроса, за
исключением его имени и значения, являются по сути - обслуживающими
(действительно, программе, которая будет анализировать передаваемую
броузером информацию, достаточно передать только имя поля формы и
его значение). Но тот факт, что эти свойства, с формальной точки
зрения, являются второстепенными, совсем снижает их важность.
Необходимо также разобраться в том, каким образом
мы будем получать информацию о свойствах критерия запроса.
Единственной информацией, которая нам требовалась ранее, было имя
запроса и источник получения этой информации очевиден - это
пользователь. Но пользователь не может задавать какие-либо свойства
критерия запроса, все свойства критерия должна распознаваться и
запоминаться незаметно для него.
В нашей задаче критерий запроса задается через
произвольный объект HTML формы. Отсюда ясно, что вся информация о
свойствах критерия запроса должна быть сокрыта именно в этом
объекте, который и будут выступать главными поставщиком информации о
свойствах критерия. Такой подход позволит достичь требуемого -
распознование свойств критериев запроса будет проходить
автоматически и незаметно для пользователя.
Конкретный способ решения этой задачи будет показан
ниже, а пока приведем формальное описание (конструктор) третьего
объекта - критерий запроса [criterion]: function criterion(obj, row)
{
this.name=obj.name; // Имя объекта (для CGI-скрипта)
this.title_name=obj.title_name; // Наименование объекта
this.value=obj.value; // Значение объекта
switch (obj.type) { // Тип объекта
case "text": { this.type="text"; break }
case "radio": {
this.type="radio";
this.id=obj.id; // Для идентификации радио-кнопки
this.title_value=obj.title_value;
break }
case "checkbox": { this.type="checkbox"; this.title_value=obj.title_value; break; }
case "select-one": {
this.type="select-one";
this.title_value=obj.options[obj.selectedIndex].text;
this.index=index;
break }
case "select-multiple": {
this.value=obj.options[row].value;
this.type="select-multiple";
this.title_value=obj.options[row].text;
this.index=obj.options[row].index;
break }
case "textarea": { this.type="textarea"; break }
}
this.service=obj.service; // Функциональное назначение объекта
this.modif = modif;
}
Еще раз подчеркнем, что этот конструктор
ориентирован на реализацию только тех требований, которые были
описаны в начале статьи. Так например, в нем отсутствует
составляющая, которая может задавать реляционные отношения между
различными критериями запроса .
К этому моменту осуществленно описание трех
JavaScript объектов, которыми будет оперировать клиентская часть
информационной системы. Этих трех объектов вполне достаточно для
полной реализации всех заявленных требований. Мы не рассматривали
подробно назначение всех методов этих объектов, поскольку сейчас
важно сформулировать общий подход, опустив второстепенные детали. С
другой стороны, количество этих методов совсем не велико, их
назначение в большинстве случаев очевидно, а программная реализация
крайне проста.
2. Режим HTML-формы
При построении развитой системы формирования
запросов к БД возникает необходимость различной обработки HTML-форм.
Наиболее распространенной задачей, как нам теперь уже известно,
будет задача запоминания новых критерив запроса, и инициализации
ранее введенных критериев. Однако эта задача не единственная.
Даже оставаясь в рамках той постановки задачи,
которая была определена выше, возникает, например, задача
переключения между различными запросами, которые могут одновременно
формироваться в системе. Например, при формировании критериев
запроса мы можем использовать форму, содержащую объект типа
select-multiple (). С другой стороны, этот же
объект select-multiple может быть использован в форме, которая будет
выводить список всех имеющихся в данный момент запросов. Очевидно,
что объекты таких форм, должны обрабатываться по разному. В первом
случае, необходимо запомнить новый критерий запроса, во втором
случае, сделать выделенный запрос текущим.
Самое очевидное и простое решение этой проблемы
заключается в введении переменной, которая будет определять режим
(способ обработки) формы. Тогда описанная выше ситуация, может быть
разрешена, например, следующим образом: <form ... rezim="1"> // для формы содержащей критерии запроса
<form ... rezim="2"> // для форма в которой выводится список запросов
В функцию же, которая будет заниматься обработкой
форм, достаточно включить следующий код: function process_of_form(obj)
{
...
if (stack.kol() != 0) { // Проверяем наличие хоть одного запроса
// Выясняем режим формы
switch (obj.rezim) {
case "1": { rezim_1(obj); break } // Функция обработки для форм с критериями запроса
case "2": { rezim_2(obj); break } // Функция обработки для формы списка запросов
default: { break }
} // end switch
} // end if
...
}
Переменная, которая передается данной функции в
качестве параметра функции, указывает на обрабатываемую форму, и
после выяснения режима обработки этой формы, вызывается
соответствующая функция. Содержимое полей форм, в которых не
прописан режим обработки, просто игнорируется.
3. Размещение свойств критерия запроса в объектах
HTML-формы
Рассмотрим теперь более подробно такую форму,
которая содержит объекты, непосредственно участвующие в формировании
критериев запроса к БД. Нас будут интересовать свойства, которые
необходимо определить в объектах такой формы, и которые мы ранее
назвали обслуживающими. Прежде чем привести содержимое одного из
таких объектов, напомним, что именно объект формы выступает в
качестве формального параметра для конструктора критерий запроса.
Итак, содержимое объекта формы <input type="text"> выглядит
следующим образом: <input type="text" name="p1_t1" title_name="Поле ввода 1" service="0" ...>
Это самый простой объект формы, и назначение
большинства переменных, которые прописаны в теге, стандартны.
Пояснения заслуживают только два свойства, которые были введены нами
дополнительно: title_name и service. Свойство (переменная)
title_name отвечает за наименование поля ввода и используется во
время просмотра критериев текущего запроса.
Переменная service задает функциональное назначение
объекта. Действительно, в информационной системе может потребоваться
и функциональное разделение критериев. В частности, может
потребоваться ввод сервисной информации, которая определяет,
например, способ вывода инормации, но никак не влияет на содержимое
самого запроса к БД. Такого рода служебная информация должна
отделяться от критериев запроса, и выводиться отдельно при просмотре
содержимого запроса. Задавая различные значения переменной service
мы решим эту задачу.
Рассмотрим теперь более сложный объект формы
<input type="radio">: <input type="radio" title_name="Радио-кнопка 1" name="p1_r1" service="0" id="p1_r1_1" title_value="Выбор 1" ...>
Помимо уже рассмотренных, его содержимое включает
дополнительные переменные: id и title_value. Переменная id
необходима для уникальной идентификации кнопок, принадлежащих одной
селекторной группе, т.к. для всех кнопок, принадлежащих такой
группе, значение переменной name должно быть одинаковым. В
переменной title_value хранится наименование конкретной селекторной
кнопки (в отличии от title_name, которое задает общее наименование
группы селекторных кнопок), которое опять же потребуется при
просмотре информации.
Представленная ниже таблица демонстрирует
необходимые дополнительные переменные для различных объектов
формы: Наименование переменной Тип объекта формы text Radio
checkbox select-one select-multiple Textarea Name + + + + + +
Title_name + + + + + + Id - + - - - - Title_value - + + - - -
Service + + + + + +
4. Алгоритмы для запоминания критериев запроса и инициализации
объектов формы.
Для наглядности преимуществ, которые дает описанный
выше подход, продемонстрируем два базовых алгоритма, которые могут
быть использованы в подобной ситеме. Внешняя простота этих
алгоритмов основывается на внутренней продуманности всей
системы.
Первый из этих алгоритмов, занимается обработкой
текстовых полей тех форм, которые содержат критерии запроса: function do_text(current,obj)
{
// Определяем количество критериев в текущем запросе
var kol=current.crit_kol()
var flag=0, i=0;
while (i<kol && flag == 0) {
if (obj.name == current.query[i].name) {
if (obj.value == "") current.del(current.query[i].name)
else current.query[i].modif(obj.value)
flag=1
}
else i++;
}
if (flag == 0 && obj.value != "") current.add(obj)
}
В качестве формальных параметров в функцию
передается две переменные: current и obj. Переменная current это
просто сокращенное название объекта текущий запрос, и может быть
определена следующим образом: current=stack.array_of_query[stack.index_query(UK)],
где UK - некоторая глобальная переменная, значением
которой является уникальный идентификатор текущего запроса. Значение
этой переменной определяется в момент обработки формы, которая
выводит список запросов, и в которой, как мы уже знаем, переменная
rezim имеет значение 2. Переменная obj,как и в функции
process_of_form, указывает на обрабатываемую форму.
Алгоритм инициализации, например, поля checkbox
также крайне прост: function ini_checkbox(current,obj)
{
var kol=current.crit_kol()
var flag=0, i=0;
while (i<kol && flag == 0) {
if (obj.name == current.query[i].name) { obj.checked=true; flag=1 }
else i++;
}
}
5. Формирование формы для отправки на сервер
Последний важный вопрос, который осталось
рассмотреть, касается создания формы для отправки на сервер. Здесь
есть два момента, на которые следует обратить внимание.
Во-первых, форма, которая будет отправляться на
сервер (т.е. форма, к которой мы будем применять метод submit() и
объекты которой будут обрабатываться CGI-скриптом) должна
создаваться в новом экземпляре броузера. Именно такой подоход
позволяет нам обеспечить независимое формирование и выполнение
произвольного числа запросов. Поэтому перед созданием формы,
необходимо применить метод open(), который создаст новый экземпляр
броузера.
Во-вторых, объекты отсылаемой на сервер формы
должны состоять только из двух полей: имени переменной (name) и ее
значении (value). Запись в форму, производится динамически, в цикле
по всем критериям запроса, и, например для имени критерия запроса,
выглядит следующим образом: doc.writeln(""+stack.array_of_query[stack.index_query(UK)].query[i].name+""),
где i - переменная цикла по всем критериям текущего
запроса.
Заключение
В настоящей работе мы рассмотрели базовые принципы,
которые могут лечь в основу построения клиентской части запросной к
базе данных системе, на основе WEB-броузера. Однако ряд важных
вопросов остался за пределами данной работы. Так например, в данной
работе подразумевалось, что каждый критерий запроса определяется
только одним объектом формы. В рамках задачи, которая была
определена в начале работы, такое предположение корректно. Однако
это ограничение снижает возможности реальной системы. Например,
существуют ситуации, когда один критерий запроса может задаваться
несколькими объектами, причем расположенными в разных формах. Это
может возникать, в частности, при выборе критериев запроса из
справочников. Также был опущен вопрос о возможности изменения
реляционных отношений между различными частями запроса. Поэтому
применяя рассмотренный выше подход, необходимо внимательно
рассмотреть и те требования, которые предъявляются реальной
системой.
|