Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Ajax_v_deystvii.pdf
Скачиваний:
34
Добавлен:
05.03.2016
Размер:
5.83 Mб
Скачать

Глава 4. Web-страница в роли приложения 173

Модель на сервере нужна в любом случае, так как именно там находятся важные ресурсы, например, база данных, средства доступа к существующим программам и т.д. Таким образом, модель предметной области на стороне клиента обязана взаимодействовать с моделью на стороне сервера. Что же из этого следует? На этот вопрос мы попытаемся ответить в главе 5, где продоляшм разговор о взаимодействии клиента и сервера. Там же мы затронем вопросы разделения модели предметной области между различными уровнями.

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

4.5. Генерация представления на основе модели

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

Создание приложений, насчитывающих несколько уровней, часто приводит к повторению информации на разных уровнях. Мы хорошо понимаем важность принципа DRY и знаем, что наилучший способ избежать дублирования — определять информацию на одном уровне и на ее основании генерировать остальные уровни. В данном разделе мы постараемся применить этот принцип на практике и опишем подход, позволяющий упростить реализацию архитектуры MVC. Обратим внимание на представление.

До сих пор мы рассматривали представление как код, созданный вручную и позволяющий отображать модель. Благодаря такому подходу мы могли достаточно свободно определять, что должен видеть пользователь. Однако в ряде случаев подобная свобода оказывается излишней, а программирование пользовательского интерфейса вручную всегда было скучным и утомительным занятием. Альтернативный подход предполагает автоматическую генерацию пользовательского интерфейса или хотя бы его компонентов на основе модели. Такой принцип уже использовался в среде Smalltalk и в системе Naked Objects Java/.NET. Язык JavaScript также вполне подходит для решения подобных задач. Рассмотрим, что можно сделать средствами отражения JavaScript, и создадим универсальный компонент "Object Browser", который может быть использован в качестве представления для любого объекта JavaScript.

4.5.1. Отражение объектов JavaScript

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

174 Часть II. Основные подходы к разработке приложений

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

код "вслепую", не имея предварительных сведений об объекте. Именно так приходится поступать при генерации интерфейса для объектов, составляющих модель. В идеале было бы неплохо реализовать универсальное решение, которое подходило бы для любой предметной области: финансовой деятельности, электронной коммерции, визуализации результатов научных исследований и т.д. В данном разделе вы ознакомитесь с JavaScript-библиотекой ObjectViewer, которую вы, может быть, захотите использовать при разработке своих приложений. Для того чтобы помочь вам составить представление об этой библиотеке, на рис. 4.7 показано несколько уровней сложного графа, отображаемых с помощью ObjectViewer.

Объект, выбранный для просмотра, представляет планету Меркурий. Этот объект достаточно сложен. Помимо обычных свойств, значениями которых являются числа или строки, он содержит URL изображения и массив с дополнительными данными. ObjectViewer обрабатывает всю информацию, не имея предварительных сведений о типе объекта.

Процесс исследования объекта, выяснения его свойств и возможностей называется отражением (reflection). Читатели, имеющие опыт работы с Java или .NET, уже знакомы с этим термином. Возможности отражения, реализованные в языке JavaScript, мы рассмотрим подробно в приложении Б. Здесь же только скажем, что объект JavaScript можно исследовать так, как будто он представляет собой ассоциативный массив. Приведенный ниже фрагмент кода выводит информацию о свойствах объекта.

var description»""; for (var i in MyObj){

var property=MyObj[i];

description+=i+" = "+property+"(BBSS)n"; } alert(description);

Представление данных посредством окна, предназначенного для вывода сообщений, — не самое удачное решение, так как это окно может не сочетаться с остальными компонентами интерфейса. Мы выбрали такой подход лишь для того, чтобы упростить пример. В листинге 4.11 представлен код ObjectViewer.

Глава 4. Web-страница в роли приложения 175

Листинг4.11.БиблиотекаObjectViewer

objviewer.ObjectViewer=function(obj,div,isInline,addNew){

styling.removeAllChildren(div); this.obj ect=obj; this.mainDiv=div; this.mainDiv.viewer=this ; this.islnline=islnline; this.addNew=addNew;

var table=document.createElement("table"); this.tbod=document.createElement("tbody"); table.appendChild(this.tbod); this.fields=new ArrayO;

this.children=new ArrayO; for (var i in this.object){

this.fields[i]=new objviewer.PropertyViewer( this, i

);

}

objviewer.PropertyViewer=function(obj ectViewer,name){ this.objectViewer=objectViewer;

this.name=name; this.value=objectViewer.object[this.name] ; this.rowTr=document.createElement("tr") ;

this.rowTr.className='objViewRow' ; this.valTd=document.createElement("td");

this.valTd.className='objViewValue' ; this.valTd.viewer=this; this.rowTr.appendChild(this.valTd) ; var valDiv=this.renderSimple(); this.valTd.appendChild(valDiv); viewer.tbod.appendChild(this.rowTr);

}

objviewer.PropertyViewer.prototype.renderSimple=function() { var valDiv=document.createElement("div");

var valTxt=document.createTextNode(this.value); valDiv.appendChild(valTxt);

if (this.spec.editable){ valDiv.className+=" editable"; valDiv.viewer=this;

valDiv.onclick=objviewer.PropertyViewer.editSimpleProperty;

}

return valDiv;

}

Всоставбиблиотекивходятдваобъекта:ObjectViewer,предназначенный Для просмотра объекта и формирования HTML-таблицы, и PropertyViewer, который отображает имя и значение отдельного свойства и представляет их

ввиде строки таблицы.

Вцелом задача решена, но существует ряд проблем. Во-первых, ОЬ- j ectViewer исследует каждое свойство. Если вы добавите к прототипу вспомогательные функции, Obj ectViewer обнаружит их. Если вы обработаете таким способом узел D O M , то увидите все встроенные свойства и сможете оценить, насколько сложен элемент D O M . В большинстве случаев нет необхо-

176 Часть II. Основные подходы к разработке приложений

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

Листинг 4.12. Применение свойства objviewspec

objviewer.ObjectViewer=function(obj,div,isInline,addNew){ styling.removeAllChildren(div) ;

this.object=obj;

this.spec=objviewer.getSpec(obj);

this.mainDiv=div;

this.mainDiv.viewer=this;

this.islnline=islnline;

this.addNew=addNew;

var table=document.createElement("table"); this.tbod=document.createElement("tbody") ; table.appendChiId(this.tbod);

this. fields=new Array (); this.children=new Array();

for (var i=0,-i<this.spec.length;i++){ this.fields[i]=new objviewer.PropertyViewer(

this,this.specfi]

);

}

objviewer.getSpec=function (obj){ return (obj.objViewSpec) ?

obj.objViewSpec : objviewer.autoSpec(obj) ;

} objviewer.autoSpec=function(obj){ var members=new Array();

for (var propName in obj){ var spec={name:propName}; members.append(spec);

}

return members;

}

obj viewer.PropertyViewer=function(obj ectViewer,memberSpec) { this.objectViewer=objectViewer;

this.spec=memberSpec;

this.name=this.spec.name;

}

Мы определили свойство objViewSpec, которое используется конструктором Objectviewer. Если данное свойство отсутствует, оно формируется в функции autoSpecO путем анализа объекта. Свойство objViewSpec представляет собой массив, каждый элемент которого является таблицей свойств. В данном случае мы генерируем только свойство name. Конструктору PropertyViewer передается описание свойства, из которого можно извлечь инструкции о порядке воспроизведения.

Предоставляя спецификацию для объекта, обрабатываемого Objectviewer, мы можем ограничить набор отображаемых свойств.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]