Статьи

GWT: построение модели

Уровень модели приложения GWT должен быть немного умнее, чем модель во многих традиционных веб-приложениях. Он должен отвечать за уведомление слоя изменений об изменениях, а также за получение обновлений для его собственной структуры. В разработке Java для настольных компьютеров это достигается за счет использования PropertyChangeEvents и PropertyChangeListeners, которые связывают данные из модели с компонентами представления. Слой вида «слушает» изменения в модели и обновляет себя соответственно, используя шаблон Observer Это можно сделать и при разработке GWT. На рисунке 1 показан базовый класс User, с которым мы будем работать. Это простой класс, содержащий имя пользователя и другие общие поля, а также два объекта Address (один отправляющий и один выставляющий счет). Для начала нам нужно включить их для привязки данных.

[Img_assist | NID = 5136 | название = | убывание = | ссылка = нет | Align = нет | ширина = 373 | высота = 142]

[Img_assist | нидь = 3421 | название = | убывание = | ссылка = URL | URL = HTTP: //www.manning.com/affiliate/idevaffiliate.php идентификатор | ALIGN = право | ширина = 208 | Высота = 388] Рисунок 1 Классы User и Address, выступающие в качестве модели. Обратите внимание на атрибут PropertyChangeSupport, который мы будем использовать для уведомления представления изменений в модельных компонентах.

проблема

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

Решение

Основой привязки данных между моделью и представлением является запуск событий в модели, которые уведомляют представление об изменениях, и наоборот. Мы будем использовать класс java.beans.PropertyChangeSupport. Этот класс еще не предоставлен GWT, но доступен как часть проекта GWTx (http://code.google.com/p/gwtx/), который добавляет к основным классам Java, предоставляемым GWT. В листинге 1 показан объект User, оснащенный классом PropertyChangeSupport.

Перечисление 1 Используя класс PropertyChangeSupport в объекте User:

public class User implements IsSerializable {                          

private String username;
private String firstName;
private String lastName;
private Address billingAddress = new Address();
private Address shippingAddress = new Address();
private String password;
private String notes;
private transient PropertyChangeSupport changes =
new PropertyChangeSupport(this);

public User() {
super();
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
String old = this.firstName;
this.firstName = firstName;
changes.firePropertyChange(
"firstName", old, firstName);
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
String old = lastName;
this.lastName = lastName;
changes.firePropertyChange("lastName", old, lastName );
}

public Address getBillingAddress() {
return billingAddress;
}

public void setBillingAddress(Address billingAddress) {
Address old = this.billingAddress;
this.billingAddress = billingAddress;
changes.firePropertyChange(
"billingAddress", old, billingAddress);
}

// The rest of the getters and setters are removed for brevity.

public void addPropertyChangeListener(
PropertyChangeListener l) {
changes.addPropertyChangeListener(l);
}

public void addPropertyChangeListener(
String propertyName,
PropertyChangeListener l) {
changes.addPropertyChangeListener(
propertyName, l);
}

public void removePropertyChangeListener(PropertyChangeListener l) {
changes.removePropertyChangeListener(l);
}

public void removePropertyChangeListener(
String propertyName, PropertyChangeListener l) {
changes.removePropertyChangeListener(propertyName, l);
}

}

Это дает нам модельный объект, способный уведомлять представление об изменениях и выставлять методы для присоединения слушателей.

обсуждение

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

Как только у нас будет класс PropertyChangeSupport, нам нужно оборудовать методы установки для запуска событий изменения. Последовательность повторяется, но очень важна. Сначала вы сохраняете текущее значение, затем вы устанавливаете новое значение, и только после того, как новое значение установлено, вы запускаете событие изменения. Крайне важно, чтобы события изменения запускались только после установки значения экземпляра, а не в начале метода установки. Причина этого заключается в том, что происходит внутри класса PropertyChangeSupport.

PropertyChangeSupport (PCS) предоставляет коллекции и запускает события изменения так же, как интерфейсы, использованные в предыдущем примере калькулятора. Он также проверяет каждый вызов firePropertyChange (), чтобы убедиться, что старое и новое значения различаются перед запуском события. На рисунке 2 показана последовательность выполнения инициируемого события изменения и обновления элемента с помощью двусторонней привязки.

[Img_assist | NID = 5137 | название = | убывание = | ссылка = нет | Align = нет | ширина = 213 | высота = 250]

Рисунок 2 Поток событий изменения свойств будет зациклен, если не проверен моделью. Если модель не проверяет изменение на равенство или если изменение еще не произошло, формируется бесконечный цикл.

Если бы вы вызвали firePropertyChange () в верхней части метода установки, когда был достигнут последний шаг в этой последовательности, текущее значение экземпляра на объекте модели все равно останется тем же, и весь цикл событий перейдет в бесконечный цикл!

Наконец, нам нужно предоставить методы для объекта модели для слушателей, которые будут добавлены в модель. Существует два типа слушателей, поддерживаемых классом PropertyChangeSupport. Глобальные слушатели будут получать событие каждый раз, когда происходит изменение. Затем слушатель будет смотреть на значение, возвращаемое вызовом getPropertyName () в экземпляре PropertyChangeEvent, чтобы решить, что делать. Другой тип слушателей зависит от свойства. Они будут запущены только в случае изменения указанного свойства, и, как правило, они являются наиболее полезными слушателями. Однако, если у вас сложная модель или модель, которая требует большого перевода между данными модели и данными, необходимыми для правильной визуализации элементов представления, может быть проще просто прослушать любое изменение и сбросить весь компонент представления.

This article is excerpted from Chapter 1 of GWT in Practice, by Robert Cooper and Charlie Collins, and published in May 2008 by Manning Publications.