Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Ajax в действии

.pdf
Скачиваний:
92
Добавлен:
01.05.2014
Размер:
6.34 Mб
Скачать

 

 

 

 

 

Глава 3. Управление кодом Ajax 125

Real

i t c h y . < / d e s c r i p t i o n >

 

< p r i c e > $ 5 . 9 9 < / p r i c e >

 

 

< c o l o r s > h e a t h e r

combo,hawaiian

medley,wild t u r k e y < / c o l o r s >

</garment>

 

 

 

<garment

id="HAT056"

t i t l e = " D e e r s t a l k e r Cap">

< d e s c r i p t i o n > C o m p l e t e

w i t h b i g

flappy b i t s .

As worn

by t h e

g r e a t

d e t e c t i v e

S h e r l o c k Holmes.

Pipe

i s

m o d e l ' s

o w n . < / d e s c r i p t i o n >

< p r i c e > $ 7 9 . 9 9 < / p r i c e >

<sizes>S , M, L, XL, egghead</sizes> </garment>

</garments>

Итак, мы получили серверную программу Web-приложения. Считаем, что Ajax-клиент способен правильно обработать полученные XML-данные. Попробуем представить себе дальнейшее развитие этой программы. Предположим, что ассортимент продукции расширился и нам надо ввести новые категории (например, Smart, Casual, Outdoor). Кроме того, необходимо реализовать поисковую функцию и установить ссылку на сервер химчистки. Очевидно, что XML-документ позволяет включить новые данные. Однако сможем ли мы повторно использовать имеющийся код и какие препятствия встретим при решении этой задачи?

Проблемы с повторным использованием кода

Организовать повторное использование сценария в том виде, в котором он существует в данный момент, непросто. Во-первых, мы "жестко" запрограммировали SQL-запрос в составе документа. Если мы захотим изменить критерии поиска, запрос нам придется генерировать по-другому. По мере добавления вариантов запроса в программе накопится большое количество выражений. Ситуация может развиваться еще хуже. Рассмотрим следующее выражение:

$sql="SELECT id,title,description,price,colors,sizes"

."FROM garments WHERE ".$sqlWhere;

Если мы разрешим передавать произвольные выражения WHERE как CGIпараметры, то можем получить следующий результат:

garments.php?sqlWhere=CATEGORY="Menswear"

Такое решение приводит к некорректной работе модели и представления, а также создает возможность для атак с применением SQL-выражений. Несмотря на то что современная система РНР имеет встроенные системы защиты, не стоит полагаться на них.

Во-вторых, мы "жестко" запрограммировали формат XML (он определяется структурой выражений printf и echo). Однако по ряду причин формат Данных может быть изменен. Не исключено, например, что руководство примет решение выводить не только цену, по которой продается товар, но и цену производителя.

126 Часть I. Новый взгляд на Web-приложение

Рис. З.7. Отношение "многие ко многим", реализованное в базе данных.

Втаблице C o l o r s перечислены значения цвета для всех изделий,

врезультате исчезает необходимость хранить информацию о цвете

втаблице Garments

В-третьих, для генерации XML-данных непосредственно используется набор результатов, полученный после обработки запроса к базе. На первый взгляд, кажется, что такое решение обеспечивает высокую эффективность работы, но при этом возможны две проблемы. Соединение с базой остается открытым в течение всего времени генерации XML-данных. В данном случае в цикле while не производятся сложные расчеты и соединение не будет открыто слишком долго, но в общем случае такое решение может стать причиной напрасного расходования ресурсов. Кроме того, подобный подход применим, только если мы рассматриваем базу данных как "плоскую" структуру.

3.4.2. Реструктуризация модели

На данном этапе работы мы храним списки цвета и размеров чрезвычайно неэффективно — помещаем значения, разделенные запятыми, в поля таблицы Garments. Чтобы преобразовать данные в соответствии с реляционной моделью, мы должны создать отдельную таблицу для хранения всех доступных значений цвета и вспомогательную таблицу, которая связывала бы изделия с цветом. Подобное отношение называется "многие ко многим" и показано на рис. 3.7.

Предположим, например, что вам нужна охотничья шляпа и вы хотите выяснить, шляпы каких цветов есть в наличии. Для этого вы обращаетесь к ключу garment_id в таблице Garments_to_Colors. Эти записи дают возможность получить первичные ключи в таблице Colors, и по ним мы видим, что в наличии имеются охотничьи шляпы цвета shocking pink и blueberry, а шляп цвета battleship gray нет в наличии. Можно сформировать и обратный запрос и использовать таблицу Garments_to_Colors, чтобы выяснить, какие изделия имеют требуемый цвет.

Глава 3. Управление кодом Ajax

127

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

Инструмент Object-Relational Mapping

Существуют средства, способные выполнить описанную задачу за нас. Для этой цели подходит набор инструментов ORM (Object-Relational Mapping), которые автоматически преобразуют данные, содержащиеся в базе, в объект, хранящийся в памяти компьютера. При этом разработчик избавлен от необходимости формировать низкоуровневые SQL-выражения. Программисты, использующие РНР, могут обратиться к PEAR DB_DataObject, EZPDO (Easy PHP Data Objects) или Metastorage. Разработчики, применяющие Java, ограничены в выборе. Им доступен лишь продукт Hibernate (теперь он перенесен и на платформу .NET). Инструменты ORM — обширная тема, рассматривать которую мы сейчас не будем.

Рассматривая наше приложение с точки зрения MVC, легко увидеть, что применение ORM дает интересный побочный эффект — с самого начала у нас в руках оказываются основы модели. Мы можем написать программу генерации XML-данных, которая будет взаимодействовать с объектом Garment, предоставив ORM самостоятельно выполнять все необходимые действия с базой. Мы больше не привязаны к API конкретной базы данных и не должны учитывать особенности ее работы. В листинге 3.7 показаны изменения кода, связанные с переходом к ORM.

В данном случае мы определяем бизнес-объекты (т.е. модель) на РНР, используя Pear: :DB_DataObject, в результате чего наши классы становятся расширением базового класса DB_DataObject. Различные ORM делают это по-разному, но в результате мы получаем набор объектов, к которым можем обращаться как к обычному коду, абстрагируясь от сложных SQL-выражений.

Листинг 3.7. Объектная модель для приложения

require_once "DB/DataObject.php";

class GarmentColor extends DB_DataObject { var $id;

var $garment_id; var $color_id;

}

class Color extends DB_DataObject { var $id;

var $name;

}

class Garment extends DB_DataObject { var $id;

var $title;

var $description; var $price;

var $colors;

128 Часть I. Новый взглядна Web-приложение

var $category; function getColors(){

if (!isset($this->colors)){ $linkObject=new GarmentColor(); $linkObject->garment_id = $this->id; $linkObject->find(); $colors=array();

while ($linkObject->fetch()){ $colorObject=new Color(),• $colorObject->id=$linkObject->color_id; $colorObject->find();

while ($colorObj ect->fetch()){ $colors[] = clone($colorObject);

}

}

}

return $colors;

}

}

Помимо основного объекта Garment, мы определяем объект Color и метод объекта Garment, предназначенный для получения всех доступных цветов. Размеры поддерживаются аналогичным способом. Поскольку библиотека непосредственно не поддерживает отношение "многие ко многим", нам надо определить объект для связующей таблицы и организовать перебор в методе getColors (). Несмотря на это модель выглядит завершенной и удобна для восприятия. Рассмотрим, как можно применить данную модель к нашему документу.

Использование обновленной модели

Мы сгенерировали модель на основе более совершенной структуры базы данных. Теперь нам надо использовать ее в нашем PHP-сценарии. В листинге 3.8 показан код страницы, использующей объекты на базе ORM.

Листинг 3.8. Страница, использующая ORM для взаимодействия с базой

<?php

header("Content-type: application/xml");

echo "<?xml version=\"l.0\" encoding=\"UTF-8\" ?>\n"; include "gannent_busines8_objects.inc"

$garment=new Garment; $garment->category = $_GET["cat"]; $number_of_rows = $garment->find(); echo "<garments>\n";

while ( $garment->fetch()) { printf("<garment id=\"%s\" title=\"%s\">\n"

. "<description>%s</descrJ^tion>\n<price>%s</price>\n", $garment->id,

$garment->title, $garment->description, $garment->price

);

Scolors»

Глава 3. Управление кодом Ajax 129

$garment->getColors(); if (count($colors)>0){ echo "<colors>\n";

for($i=0; $i<count($colors);$i++){ echo "<color>{$colors[$i]}</color>\n";

}

echo "</colors>\n";

}

echo "</garment>\n";

}

echo "</garments>\n"; ?>

Вместо того чтобы конструировать SQL-запросы, создается пустой объект Garment, который затем частично заполняется данными, представляющими собой критерии поиска. Поскольку модель включается из отдельного файла, мы можем повторно использовать ее для поиска. Средства представления XML также генерируются на основе модели. Наши последующие действия по реструктуризации направлены на разделение XML-данных и процесса их генерации.

3.4.3. Разделение содержимого и представления

Код представления пока еще не отделен от объекта. Причина в том, что формат XML связан с кодом, предназначенным для его анализа. Если мы работаем с несколькими страницами, будет полезно, если мы сможем изменить XML-формат в одном месте приложения, оставив другие фрагменты без изменений. В более сложном случае может понадобиться поддержка нескольких форматов, например, один для представления перечня товаров пользователю, а другой для управления самим магазином. Поэтому желательно определить каждый формат только один раз и обеспечить для них централизованное отображение.

Системы на базе шаблонов

Для решения данной задачи используется язык шаблонов. Система, поддерживающая шаблоны, получает текстовый документ, содержащий специальную разметку. Элементы разметки обозначают позиции реальных переменных. К языкам шаблонов можно отнести PHP, ASP и JSP, которые позволяют включать фрагменты кода в содержимое Web-страниц. Этим они принципиально отличаются от кодов, включающих содержимое, примерами которых являются Java-сервлеты или традиционные сценарии CGI. Несмотря на то что сценарии предоставляют достаточно мощные средства обработки запросов, используя их, очень трудно разделить бизнес-логику и представление.

Языки сценариев, ориентированные на конкретное применение, например PHP Smarty и Apache Velocity (система на базе Java; при переносе на платформу .NET она получила название N Velocity), предоставляют ограниченные возможности для создания кода. В ряде случаев поток управления

130 Часть I. Новый взгляд на Web-приложение

может содержать лишь ветвление (оператор if) и циклы (например, операто-

 

ры for и while). В листинге 3.9 показан шаблон PHP Smarty для генерации

I

XML-данных.

 

<?xml version="1.0" encoding="UTF-8" ?>

 

<garments>

 

{section name=garment loop=$garments}

 

<garment id="{$garment.id}" title="{$garment.title}">

 

<description>{$garment.description}</description>

 

<price>{$garment.price}</price>

 

(if count($garment.getColors())>0}

 

<colors>

 

{section name=color loop=$garment.getColors()}

 

<color>$color->name</color>

 

{/section}

 

</colors>

 

{/if}

 

</garment>

 

{/section}

 

</garments>

_

Входной информацией для шаблона является переменная массива gar-

 

ments, содержащего объекты Garment. Блыпая часть шаблона генерируется

 

процессором, а разделы в фигурных скобках интерпретируются как инструк-

 

ции. Они либо заменяются именами переменных, либо интерпретируются как

 

выражения ветвления и циклов. Структура выходного XML-документа, вы-

 

раженная в виде шаблона, воспринимается гораздо лучше, чем код, подобный

 

тому, который был представлен в листинге 3.7. Теперь поговорим о том, как

 

применить шаблон к нашему документу.

 

Использование обновленного представления

Мы переместили определение XML-данных из основного документа в шаблон Smarty. В результате главная страница должна лишь установить процессор шаблона и передать необходимые данные. В листинге 3.10 отражены изменения, необходимые для этого.

Листинг 3.10. Использование Smarty для генерации XML-данных

<?php

header("Content-type: application/xml"); include "garment_business_objects.inc"; include "smarty.class.php";

$garment«new DataObjects_Garment; $garment->category = $_GET["cat"] ; $number_of_rows - $garment->find(); $smarty-new Smarty;

$3marty->assign('garments', $garments); $smarty->display('garments_xml.tpl'); ?>

Глава 3. Управление кодом Ajax 131

рис. 3.8. Архитектура MVC применима к Web-приложениям. Web-страница или сервлет действует как контроллер и обращается к модели для получения

данных. Затем эти данные передаются 1а йлу шаблона (представление), который генерирует содержимое, предназначенное для передачи клиентской программе. Заметьте, что в данной ситуации предусмотрено только чтение данных. При модификации модели поток событий несколько изменится, но роли составных частей приложения останутся прежними

Обычно действия с шаблонами насчитывают три этапа. Сначала мы создаем шаблон Smarty, затем заполняем его переменными. В данном случае переменная только одна, но мы можем использовать их столько, сколько нам надо. Например, если информация о пользователе хранится вместе с данными о сеансе, мы можем сформировать посредством шаблона персональное приветствие. После этого мы вызываем функцию display () и передаем ей имя файла шаблона.

Итак, нам удалось отделить представление от страницы с результатами поиска. Формат XML определяется только один раз, и мы можем организовать воспроизведение данных несколькими строками кода. Страница с результатами поиска теперь ориентирована на решение одной задачи и содержит только необходимую для этого информацию, а именно: параметры поиска и определение выходного формата. Если вы помните, мы хотели обеспечить возможность легко переходить к форматам, альтернативным XML. С помощью Smarty это достигается просто, мы лишь определяем дополнительный формат. Если мы хотим реализовать структуру, обеспечивающую минимум изменений, мы можем даже использовать шаблон внутри другого шаблона.

Если вы вспомните, в чем состоит архитектура "модель-представление- контроллер", то увидите, что наше приложение соответствует ей. Реализация приложения условно показана на рис. 3.8.

В нашем наборе объектов постоянное хранение модели в базе данных обеспечивается посредством ORM. Представлением служит шаблон, определяющий XML-формат. Контроллер — это страница "поиска по категории" и другие документы, которые мы создадим и которые будут объединять модель и представление.

Мы рассмотрели пример применения архитектуры "модель-представление- контроллер" к Web-приложению. Мы обсуждали серверные программы приложения Ajax, поддерживающего XML-документы, но нетрудно понять, как применить данную архитектуру к классическому Web-приложению, поддерживающему HTML-документы.

В зависимости от технологий, возможны вариации данного образа разработки, но общие принципы остаются теми же. Компоненты Enterprise Jav-

132 Часть I. Новый взгляд на Web-приложение

aBeans, поддерживаемые в J2EE, реализуют модель и контроллер и даже позволяют помещать их на различные серверы. Классы .NET делегируют функции контроллера объектам, специфическим для конкретных страниц. Базовые средства из библиотек типа Struts определяют контроллер переднего плана (front controller), который перехватывает все запросы к приложению и перенаправляет их. При использовании Apache Struts возможно решение, при котором контроллер будет переадресовывать пользователя от одной страницы к другой. Но в любом случае общие принципы архитектуры остаются неизменными. Это одна из причин популярности MVC среди разработчиков Web-приложений.

Архитектура "модель—представление-контроллер" упрощает работу над программами для Web и вполне применима не только для классических, но и для Ajax-приложений. Однако в Ajax архитектура MVC используется не единственным способом. В главе 4 мы рассмотрим разновидности данного образа, обеспечивающие преимущества структурированной разработки для всех компонентов приложения. Однако сначала рассмотрим еще один способ упорядочения Ajax-приложений.

Помимо реструктуризации кода, мы можем упростить работу над ним, используя базовые средства и библиотеки независимых разработчиков. По мере возрастания интереса к Ajax появляются новые средства разработки, j Рассмотрением наиболее популярных из них мы завершим данную главу.

3.5. Библиотеки независимых производителей

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

На протяжении этой книги мы время от времени будем создавать простые, но универсальные средства разработки. Вы можете использовать их при работе над своими проектами. В главах 4 и 5 мы разработаем объект ObjectViewer, в главе 5 — объект CommandQueue, в главе 6 — средства оповещения, в главе 9 — элемент профилирования Stopwatch, а в приложении А — отладочную консоль. В конце глав 9 и 13 будут рассмотрены несложные примеры. Там же мы реструктуризируем созданные программы, превратив их в компоненты, пригодные для повторного применения.

Очевидно, что не мы одни интересуемся Ajax и JavaScript, поэтому многие средства для работы с ними, многократно проверенные разработчиками, сейчас доступны в Интернете.

В этом разделе мы рассмотрим некоторые библиотеки и наборы базовых средств, которые были созданы независимыми производителями и доступны для использования в Ajax-приложениях. На сегодняшний день популяр-

Глава 3. Управление кодом Ajax 133

ность Ajax высока, поэтому мы не можем детально описать все приложения и постараемся дать вам лишь общее представление о том, какие продукты существуют и как они помогут вам навести порядок в коде ваших проектов.

3.5.1. Библиотеки, обеспечивающие работу с различными браузерами

Как было сказано в разделе 3.2.1, несоответствие браузеров не исчезло с появлением Ajax-приложений. Ряд библиотек предлагает функции, позволяющие скрыть несоответствие браузеров, рекомендуя единую точку обращения к соответствующим средствам (другими словами, они реализуют образ разработки Fagade). Одни из них ориентированы на конкретные функции, другие пытаются предоставить среду программирования с более развитыми возможностями.

Библиотека х

Библиотека х предназначена для решения задач, возникающих при написании DHTML-приложений. Она была впервые представлена в 2001 году и является развитием более ранней библиотеки СВЕ (Cross-Browser Extensions). Эта библиотека предоставляет функции, позволяющие обрабатывать в различных браузерах элементы DOM и стили, работать с моделями событий, а также поддерживает анимацию и перетаскивание объектов с помощью мыши. Библиотека х допускает работу с Internet Explorer, начиная с версии 4, и с последними версиями браузеров Opera и Mozilla.

Стиль кодирования библиотеки х основан на использовании функций; причем переменное количество параметров и отсутствие поддержки типов позволяют извлечь дополнительные преимущества. Например, для метода document.getElementById(), обрабатывающего только строковые данные, создана оболочка, которая принимает как строки, так и элементы DOM. Если передана строка, осуществляется преобразование идентификатора, а элемент DOM возвращается в неизменном виде. В результате вызов xGetElementByld() гарантирует, что параметр будет преобразован в узел DOM; при этом отпадает необходимость выполнять проверку и использовать условный оператор. Возможность замены элемента DOM текстовым идентификатором полезна при динамической генерации кода, например, при передаче строки методу setTimeout () либо при организации обратного вызова.

При работе со стилями для элементов DOM используются функции, посредством которых можно как получать, так и устанавливать значение. Рассмотрим следующее выражение:

xWidth(myElement)

В результате выполнения функции возвращается значение ширины для элемента DOM (где myElement — либо элемент, либо его идентификатор). Добавляя дополнительный параметр, можно использовать ту же функцию Для установки значения ширины.

xWidth(myElement,420)

134 Часть I. Новый взгляд на Web-приложение

 

Следовательно, чтобы задать ширину элемента равной ширине другого

элемента, надо использовать выражение.

 

xWidth(secondElement,xWidth(firstElement))

 

Библиотека х не содержит кода для передачи запросов по сети, но

она

может быть полезна при создании пользовательских интерфейсов

для

Ajax-приложений.

Библиотека Sarissa

Библиотека Sarissa в основном ориентирована на работу JavaScript-сценариев с XML-данными. Она поддерживает MSXML ActiveX-компоненты Internet Explorer (начиная с версии 3) и базовые функции Mozilla, Opera, Konqueror и Safari. Расширенные средства, такие как XPath и XSLT, поддерживаются не для всех указанных браузеров.

Для разработчиков Ajax-приложений наиболее важными функциями являются поддержка объекта XMLHttpRequest в различных браузерах. Вместо того чтобы создавать объект типа Facade, Sarissa использует образ разработки Adapter для создания объекта XMLHttpRequest в тех браузерах, в которых отсутствует встроенный объект с таким именем (в частности, Internet Explorer). Код, реализующий описанные средства, конечно же, обращается к объектам ActiveX так, как было описано в главе 2, но эти действия остаются невидимыми для разработчика. Например, после импортирования библиотеки Sarissa следующий фрагмент кода будет выполняться независимо от используемого браузера:

var xhr = new XMLHttpRequest(); xhr.open("GET", "myData.xml"); xhr.onreadystatechange = function(){ if(xhr.readyState == 4){ alert(xhr.responseXML);

}

}

xhr.send(null);

Сравните этот фрагмент с кодом, представленным в листинге 2.11, и заметьте, что вызовы функций API осуществляются так же, как и в браузерах Mozilla и Safari, содержащих встроенный объект XMLHttpRequest.

Как было сказано ранее, Sarissa также представляет универсальные средства для работы с XML-документами, в частности, позволяет выполнять сериализацию произвольных объектов JavaScript и представлять их в формате XML. Данный механизм может быть использован для обработки XMLцанных, полученных с сервера в ответ на запрос. (Эту задачу и возможные ее решения мы рассмотрим в главе 5.)

Библиотека Prototype

Библиотека Prototype содержит функции общего назначения для программ аа JavaScript. Основное внимание разработчики библиотеки уделили расширению возможностей самого языка и поддержке объектов. Дополнительные