Статьи

Дизайн моделей и GWT

[Img_assist | нидь = 3421 | название = | убывание = | ссылка = URL | URL = HTTP: //www.manning.com/affiliate/idevaffiliate.php идентификатор | ALIGN = влево | ширина = 208 | высота = 388] Мы У нас есть реализация калькулятора EntryPoint, которая помещает виджет CalculatorWidget в RootPanel, поэтому нам нужно предоставить этот виджет. Это будет наш собственный виджет, состоящий из основных компонентов GWT. CalculatorWidget будет содержать представление для нашего приложения калькулятора, простой слой данных и логики в качестве модели и промежуточный контроллер между этими слоями. Это, конечно, означает, что мы будем использовать MVC и GWT вместе — имейте в виду, что все эти компоненты будут в конечном итоге распространяться на клиента.

MVC будет всеобъемлющей моделью для макроуровня нашего приложения. Кроме того, мы будем использовать некоторые другие общие объектно-ориентированные (ОО) шаблоны на других уровнях из-за общих преимуществ, которые обеспечивают эти подходы. Например, мы будем использовать шаблон Observer для обработки событий как часть механизма связи между нашей моделью и нашим представлением. Мы также будем использовать шаблон «Стратегия» в части логики и операций нашей модели. Мы подробно расскажем о каждом из этих шаблонов и о причинах их использования в каждой ситуации, когда мы подойдем к этим частям приложения.

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

MVC и GWT

MVC — знакомая модель для многих разработчиков. Паттерн был впервые описан в конце 70-х годов Трюгве Реенскаугом, который в то время работал над Smalltalk, и с тех пор он был принят в различных формах широким кругом программистов, использующих много языков. Шаблон предназначен для разделения обязанностей и допускает повторное использование логики и части данных приложения — модели. Слой представления представляет модель графически, как пользовательский интерфейс. Делегирование между этими уровнями обрабатывается посредником, который вызывается пользовательским вводом, контроллером. Основная схема MVC показана на рисунке 1.

[Img_assist | NID = 4137 | название = | убывание = | ссылка = нет | Align = нет | ширина = 128 | высота = 84]

Рисунок 1. Базовая схема архитектурного шаблона модель-представление-контроллер. Сплошные линии указывают на прямую связь, а пунктирные линии указывают на косвенную связь, например, через отношение наблюдатель / наблюдаемый.

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

В MVC модель может быть оборудована для оповещения наблюдателей об изменениях внутри нее, используя шаблон Observer (что мы рассмотрим более подробно в разделе об общении путем наблюдения за событиями). Это показано пунктирной линией на рисунке 1. Таким образом, любой другой слой, наблюдающий модель, может реагировать на события, происходящие из модели. Тем не менее, модель напрямую не обновляет представление, и это различие важно. Модель является частью многократного использования во всех воплощениях MVC; если бы модель имела непосредственное знание о представлении, этот принцип сломался бы. То, как представление и контроллер связаны друг с другом, часто более изменчиво.

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

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

Возможные варианты MVC и многие похожие архитектурные шаблоны GUI, такие как Presentation Abstraction Control (PAC) и Model View Presenter (MVP), очень интересны, но выходят за рамки нашей области. К счастью, целые тома на эту тему существуют. Для наших целей мы обратимся к конкретным примерам MVC, которые, как нам показалось, хорошо работают с GWT. Для этого нам нужно войти в код и реализовать шаблон.

Создание виджета

Функциональные характеристики калькулятора (по крайней мере, простой) довольно универсальны; они включают в себя отображение, некоторые кнопки и некоторые математические операции, определенные как Пифагор и Евклид. Для нашего калькулятора мы будем использовать один текстовый блок GWT для дисплея и множество кнопок GWT для, ну, в общем, кнопок. Мы также будем использовать макет, полученный из Panel и Grid, для управления общим размещением элементов пользовательского интерфейса. С точки зрения MVC, элементы представления — это то место, где начинаются все виджеты, начиная с макета. Модель и контроллер для нашего калькулятора будут реализованы в виде отдельных классов, на которые ссылается виджет.

С GWT вы используете виджеты для создания своего приложения. Эти компоненты построены из панелей макета, элементов ввода, событий, объектов данных и их различных комбинаций. Именно здесь GWT действительно отходит от типичных веб-приложений и может показаться чем-то чуждым для тех, кто привык к стандартной разработке Java на стороне сервера. Для создания приложения вы используете виджеты, которые можно вставлять или удалять с экрана на одной странице на стороне клиента.

В наших целях мы расширим предоставляемый GWT компонент VerticalPanel для построения нашего представления, а затем включим ссылки на наш отдельный контроллер и модель, как показано на диаграмме классов на рисунке 2.

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

Рисунок 2. Диаграмма классов верхнего уровня CalculatorWidget, показывающая расширение VerticalPanel GWT в качестве представления и включенные ссылки на модель и контроллер

Стоит отметить, что на рисунке 2 не отображаются все атрибуты или операции класса CalculatorWidget. Мы включили только репрезентативную выборку атрибутов, чтобы сделать ее краткой. Код для CalculatorWidget будет отображаться в трех частях в листингах 1, 2 и 3. В листинге 1 показано начало класса, показывающее, что класс должен импортировать, и как класс извлекается из других и использует их. существующие классы GWT.

Листинг 1 CalculatorWidget.java, часть первая:

package com.manning.gwtip.calculator.client;

import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.TextBox;

//. . . remainder of imports omitted

public class CalculatorWidget extends VerticalPanel {
private TextBox display;

public CalculatorWidget(final String title) {
super();

final CalculatorData data = new CalculatorData();

final CalculatorController controller =
new CalculatorController(data);

//...

В первой части класса CalculatorWidget есть несколько важных элементов. Прежде всего, мы используем некоторые существующие классы GWT client.ui для составления нашего представления. Фактически весь наш класс является подклассом VerticalPanel. Это важно, поскольку мы автоматически наследуем всю иерархию виджета пользовательского интерфейса GWT с помощью этого подхода.

Прямое расширение панели таким способом является простым (в данном случае, намеренно), но оно также ограничивает. Часто лучше использовать GWT Composite. Мы используем прямое создание подклассов, чтобы сделать вещи очень простыми, и мы со временем перейдем к Composite.

В листинге 1 мы затем определяем простой конструктор и внутри него создаем экземпляр CalculatorController. Сам калькуляторController будет рассмотрен в разделе об управлении действием после того, как мы завершим класс CalculatorWidget. Контроллер, который мы будем использовать, является клиентским компонентом, который сам содержит ссылку на часть данных нашей модели, которая является единственным объектом CalculatorData. Это подводит нас ко второй части класса CalculatorWidget: макет и кнопки. Эти элементы просмотра отображаются в листинге 2.

Перечисление 2 CalculatorWidget.java, часть вторая:

VerticalPanel p = new VerticalPanel();
p.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT);
p.setStyleName(CalculatorConstants.STYLE_PANEL);

Grid g = new Grid(4, 5);
g.setStyleName(CalculatorConstants.STYLE_GRID);

final Button zero = new ButtonDigit(controller, "0");
g.setWidget(3, 0, zero);

final Button one = new ButtonDigit(controller, "1");
g.setWidget(2, 0, one);

final Button two = new ButtonDigit(controller, "2");
g.setWidget(2, 1, two);

//. . .

final Button divide = new ButtonOperator(controller, new OperatorDivide());
g.setWidget(0, 3, divide);

final Button multiply = new ButtonOperator(controller, new OperatorMultiply());
g.setWidget(1, 3, multiply);

//. . .

final Button clear = new Button(CalculatorConstants.CLEAR);
clear.addClickListener(new ClickListener() {
public void onClick(Widget sender) {
controller.processClear();
}
});
clear.setStyleName(CalculatorConstants.STYLE_BUTTON);
g.setWidget(2, 4, clear);

//...

Во второй части класса CalculatorWidget мы добавляем компоненты в наш виджет и добавляем обработчики событий для этих компонентов. Сначала мы создаем GWT Grid, компонент, состоящий из строк и столбцов, который в конечном итоге будет реализован в виде таблицы HTML. Мы будем использовать это, чтобы выложить наши кнопки калькулятора. Затем мы добавляем два разных типа кнопок: ButtonDigit и ButtonOperator. Цифры в нашем калькуляторе реализованы нашим собственным классом ButtonDigit, который расширяет класс GWT Button. Операторы аналогичным образом реализованы нашим собственным классом ButtonOperator. Еще раз, ради краткости, мы не включили код для всех достаточно понятных кнопок и операторов.

Нам еще предстоит создать ButtonDigit и ButtonOperator, что мы и сделаем дальше, но важно знать, что оба эти класса реализуют ClickListener. Этот слушатель получает элемент управления при нажатии кнопок и вызывает метод контроллера. Слушатели распространены в компонентно-ориентированной разработке и обеспечивают подход, основанный на событиях. В GWT доступны различные слушатели, такие как ClickListeners, OnChangeListeners, KeyboardListeners и другие. Используя слушателей таким образом, контроллер уведомляется о необходимости выполнить соответствующее действие, когда на нашем калькуляторе нажимается какая-либо кнопка или оператор.

О KeyboardListeners

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

Наконец, мы также добавили стандартную кнопку GWT для функций CLEAR и EQUALS нашего калькулятора. Эти кнопки напрямую вызывают методы нашего экземпляра контроллера без нашего собственного подкласса Button. Это сделано в отношении CLEAR и EQUALS, потому что это особые случаи с точки зрения кнопок калькулятора, как мы увидим, когда перейдем к контроллеру в следующем разделе. Однако прежде чем двигаться дальше, нам нужно реализовать оставшуюся часть CalculatorWidget, как показано в листинге 3.

Перечисление 3 CalculatorWidget.java, часть третья:

// . . .

display = new TextBox();

data.addChangeListener(new CalculatorChangeListener() {
public void onChange(CalculatorData data) {
display.setText(
String.valueOf(data.getDisplay()));
}
});
display.setText("0");
display.setTextAlignment(TextBox.ALIGN_RIGHT);

p.add(display);
p.add(g);
this.add(p);
}
}

Последняя часть класса CalculatorWidget создает текстовое поле GWT для области отображения нашего калькулятора, а затем добавляет этот компонент и нашу сетку в ранее созданный VerticalPanel, который, в свою очередь, добавляется к текущему самому экземпляру. Это гарантирует, что все элементы видны при использовании CalculatorWidget.

Возвращаясь к обработке событий, мы также представили пользовательский слушатель — наш собственный интерфейс CalculatorChangeListener. Это реализовано как анонимный внутренний класс, и он соединяет наш экранный виджет (представление) с данными, которые он представляет (модель), посредством событий.

Эти события являются проявлением паттерна Observer. Теперь мы подробнее рассмотрим эти отношения, потому что именно это облегчает разделение обязанностей в нашем калькуляторе GWT на базе MVC.

Общение путем наблюдения за событиями

Нам нужно кратко остановиться на обработке событий, которую мы только что представили через CalculatorChangeListener. Общая схема Observer — это то, как будут связаны модель и представление нашего калькулятора. Это также, как обычно общаются компоненты GWT по умолчанию.

В дополнение к MVC, многие разработчики привыкли к обработке событий. Некоторые программисты, не использующие GUI, возможно, также осознали всю мощь этого подхода и могут использовать его на стороне сервера. Если вы уже знакомы с событиями, вы можете найти наши первоначальные примеры здесь примитивными. Это потому, что мы пытаемся упростить и продемонстрировать концепции, а не создавать наиболее эффективный или оптимизированный код. (Например, мы не используем такие стандартные идиомы событий Java, как PropertyChangeSupport и связанные конструкции.).

Этот подход должен быстро привести в движение тех, кто еще не сталкивался с основанным на событиях и асинхронным программированием, особенно в отношении графического интерфейса пользователя. Сам шаблон будет выведен на первый план с нашим первым примером с ручным управлением. Прослушивание событий и уведомление о них — все это аспекты модели Observer. Рисунок 3 иллюстрирует эту схему.

[Img_assist | нидь = 4139 | название = | убывание = | ссылка = нет | Align = нет | ширина = 356 | Высота = 177]

Рисунок 3 Шаблон Observer, который используется в GWT для соединения модели и представления

В нашем примере CalculatorChangeListener используется для регистрации нашего компонента представления (самого нашего CalculatorWidget) в части данных нашей модели (CalculatorData). Это в основном первая половина отношения наблюдатель / наблюдаемый между этими компонентами. Наш интерфейс прослушивателя изменений показан в листинге 4.

Перечисление 4 CalculatorChangeListener.java

public interface CalculatorChangeListener {
public void onChange(CalculatorData data);
}

CalculatorChangeListener имеет единственный метод обратного вызова, onChange (). Это довольно типичный базовый интерфейс слушателя. Наш компонент модели CalculatorData, как мы увидим чуть позже, делает себя наблюдаемым (доступным для прослушивания слушателями), реализуя другой простой интерфейс, CalculatorChangeNotifier. Этот интерфейс уведомлений об изменениях показан в листинге 5.

Перечисление 5 CalculatorChangeNotifier.java

public interface CalculatorChangeNotifier {
public void addChangeListener(
final CalculatorChangeListener listener);
}

Поэтому CalculatorData, как показано в листинге 6, выполняет вторую половину отношения наблюдатель / наблюдаемый и уведомляет всех слушателей, которые появляются, когда что-то меняется.

Перечисление 6 CalculatorData.java: часть данных слоя модели:

public class CalculatorData implements CalculatorChangeNotifier {
private String display;
private double buffer;
private boolean initDisplay;
private boolean lastOpEquals;
private CalculatorChangeListener listener;

public CalculatorData() {
this.clear();
}

public void addChangeListener(
final CalculatorChangeListener listener) {
this.listener = listener;
}

public void clear() {
this.display = "0";
this.buffer = 0.0;
this.initDisplay = true;
this.lastOpEquals = false;
if (listener != null) listener.onChange(this);
}

public double getBuffer() {
return buffer;
}

public void setBuffer(double buffer) {
this.buffer = buffer;
listener.onChange(this);
}

public String getDisplay() {
return display;
}

public void setDisplay(String display) {
this.display = display;
listener.onChange(this);
}

//...

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

Наряду с механизмом событий вы, вероятно, уже заметили, что CalculatorWidget делает ссылки на CalculatorConstants. Это простой класс констант, который определяет стили CSS и строковые константы, такие как: ADD, SUBTRACT, SQRT и EQUALS. Теперь, когда у нас есть наш CalculatorWidget и наша обработка событий позаботилась, мы вернемся к оставшейся части нашего примера. Это означает, что нам нужно обратиться к ButtonDigit и ButtonOperator, где мы встретимся с другой половиной нашей модели, логикой.

Операторская стратегия

Как мы видели в коде в разделе «Создание виджета», CalculatorWidget использует несколько пользовательских типов кнопок. Кнопки калькулятора обрабатывают числовой и операторский ввод. Кнопки, конечно, являются компонентами просмотра, но кнопки, которые мы создадим для выполнения этих ролей, также поддерживаются логической частью нашей модели, операторами нашего калькулятора. Мы использовали наши собственные конкретные типы Java, ButtonDigit и ButtonOperator, чтобы мы могли легко различать логические типы нажатых кнопок и чтобы мы могли дополнительно инкапсулировать логику для каждой операции. В листинге 7 показана наша реализация ButtonDigit.

Перечисление 7 ButtonDigit.java

public class ButtonDigit extends Button {
public ButtonDigit(
final CalculatorController controller,
final String label) {
super(label);
this.addClickListener(new ClickListener() {
public void onClick(Widget sender) {
controller.processDigit(label);
}
});
this.setStyleName(CalculatorConstants.STYLE_BUTTON_DIGIT);
}
}

ButtonDigit — это очень простое расширение класса GWT Button. Он просто включает в свой конструктор ссылку на объект CalculatorController, а затем вызывает метод контроллера processDigit () при каждом нажатии на экземпляр ButtonDigit. Это повторение шаблона, который мы используем во внешнем виджете CalculatorWidget, используя ту же ссылку на контроллер. Этот вызов достигается с помощью ClickListener.

Основной момент в этом примере, особенно для разработчиков, ориентированных на сервер, заключается в том, что расширение базовых классов пользовательского интерфейса не только возможно, но и желательно. Использование методов ООП для расширения функциональности обеспечивает более чистое разделение и повторное использование кода. Это отличается от разработки на основе HTML, где <input type = «button»> представляет непрозрачную инструкцию. Этот довольно простой пример просто передает значение, но вы должны заметить, что шаблон MVC присутствует на нескольких уровнях.

В листинге 8 мы снова видим этот шаблон с классом ButtonOperator. В этом случае MVC вступает в игру на микроуровне, а также на макроуровне со всем внешним виджетом. ButtonOperator имеет модель, представленную меткой; представление, управляемое родительским классом Button, подправленное назначением стиля для кнопки; и уровень контроллера, представленный экземпляром AbstractOperator, который транслирует действия вплоть до контроллера большего объема для виджета калькулятора.

Перечисление 8 ButtonOperator.java

public class ButtonOperator extends Button {
public ButtonOperator(final CalculatorController controller,
final AbstractOperator op) {
super(op.label);
this.addClickListener(new ClickListener() {
public void onClick(Widget sender) {
controller.processOperator(op);
}
});
this.setStyleName(CalculatorConstants.STYLE_BUTTON_OPERATOR);
}
}

ButtonOperator работает почти так же, как ButtonDigit. Он расширяет класс GWT Button и включает ссылку на объект CalculatorController в своем конструкторе. Затем он вызывает метод processOperator () контроллера при каждом нажатии на экземпляр ButtonOperator. Разница с ButtonOperator заключается в том, что он включает ссылку на класс AbstractOperator в своем конструкторе.

Теперь, когда у нас есть кнопки оператора, нам, очевидно, нужны операции для поддержки этих компонентов представления. Для завершения этих компонентов мы будем использовать шаблон Стратегии для инкапсуляции логики. Вместо монолитного логического блока if / else мы делегируем каждую операцию определенному экземпляру класса операторов (каждый из которых является реализацией AbstractOperator) и позволяем этим классам обновлять модель нашего калькулятора. Использование наших операторов таким образом облегчает понимание нашего калькулятора и обеспечивает большую гибкость и расширяемость. При таком подходе мы можем добавлять новые операторы позже, не влияя на нашу существующую логику. На рисунке 4 показан шаблон стратегии.

[Img_assist | NID = 4140 | название = | убывание = | ссылка = нет | Align = нет | ширина = 356 | Высота = 86]

Рисунок 4 Шаблон стратегии, используемый в примере калькулятора GWT для реализации операторов

Единственными исключениями из этой стратегической структуры для операторов являются операции CLEAR и EQUALS, которые обрабатываются непосредственно контроллером калькулятора, а не подклассами операторов. Выполнение этого требует меньшего количества кода и делает вещи немного яснее. (У нас есть некоторая глобальная информация о состоянии в контроллере, на которую полагаются CLEAR и EQUALS. Реализация пуриста может поместить эту информацию в нашу модель и сделать отдельные операторы для CLEAR и EQUALS, но это усложнит другие операции только ради шаблон, и мы думаем, что это будет излишним в этом случае.)

Эти концепции могут показаться немного за пределами проторенного пути GWT, но они служат для демонстрации важного момента, что вы можете использовать многие принципы проектирования ОО в контексте инструментария. Эта гибкость является одним из преимуществ GWT. Кроме того, этот подход обеспечивает гораздо более надежный калькулятор в конце дня.

Чтобы обработать отдельные операторы для нашего калькулятора, нам нужно реализовать AbstractOperator и подклассы, которые его расширят. Как мы видели в листинге 8, ButtonOperator включает ссылку на AbstractOperator. Это потому, что каждый оператор должен делать что-то свое в приложении калькулятора; у каждого свое поведение. AbstractOperator, прямое начало иерархии, показано в листинге 9.

Перечисление 9 AbstractOperator.java

public abstract class AbstractOperator {
public String label;

AbstractOperator(final String label) {
this.label = label;
}
public abstract void operate(
final CalculatorData data);
}

AbstractOperator определяет один абстрактный метод, operator (), который принимает CalculatorData в качестве входных данных и соответственно обновляет его. Далее мы делим операторы с абстрактными типами, которые определяют, является ли реализуемый оператор двоичным или унарным. BinaryOperator и UnaryOperator являются следующими:

public abstract class BinaryOperator extends AbstractOperator {
BinaryOperator(final String label) {
super(label);
}

public abstract class UnaryOperator extends AbstractOperator {
UnaryOperator(final String label) {
super(label);
}
}

С нашими абстракциями операторов теперь мы добавим простой конкретный оператор для добавления. (Для краткости мы не будем явно охватывать все конкретные операторы, используемые в нашем CalculatorWidget в тексте). В листинге 10 отображается OperatorAdd.

Перечисление 10 OperatorAdd

public class OperatorAdd extends BinaryOperator {

public OperatorAdd() {
super(CalculatorConstants.ADD);
}

public void operate(final CalculatorData data) {
data.setDisplay(String.valueOf(data.getBuffer() +
Double.parseDouble(data.getDisplay())));
data.setInitDisplay(true);
}
}

Оператор двоичного сложения добавляет текущий буфер к текущему отображаемому значению, а затем обновляет отображение. Бинарные операторы в этом контексте в основном отвечают за обновление отображения на основе значений в объекте CalculatorData. Кроме того, этот оператор устанавливает для состояния initDisplay значение true; это означает, что при вводе следующей цифры дисплей должен начинаться сначала, а не добавлять цифры к любому возможному существующему значению. Теперь, когда все остальные аспекты нашего виджета Calculator установлены, мы можем перейти к контроллеру.

Контролировать действие

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

Компонент контроллера GWT CalculatorWidget имеет большое значение по нескольким причинам, помимо разделения обязанностей, вытекающих из самого шаблона MVC. Мы напишем этот компонент на Java, и он будет скомпилирован в JavaScript GWT вместе с нашими компонентами пользовательского интерфейса, но это не имеет никакого отношения к интерфейсу. GWT позволяет вам создавать не только ваш пользовательский интерфейс, но также любые логические представления и представления данных с намерениями на стороне клиента. Вы можете создавать классы, как мы собираемся сделать с нашим CalculatorController и уже сделали с нашими CalculatorData, которые заканчиваются в клиентском браузере как полностью не связанные с просмотром элементы.

В CalculatorController, который представлен в листинге 11, обратите внимание, что нет ссылок на классы, связанные с GWT.

Перечисление 11 CalculatorController.java

public class CalculatorController {

CalculatorData data;
AbstractOperator lastOperator;
Private double prevBuffer;

public CalculatorController(
final CalculatorData data) {
this.data = data;
}

public void processClear() {
data.clear();
lastOperator = null;
}

public void processEquals() {
if (lastOperator != null) {
if (!data.isLastOpEquals()) {
prevBuffer = Double.parseDouble(data.getDisplay());
}
lastOperator.operate(data);
data.setBuffer(prevBuffer);
data.setLastOpEquals(true);
}
}

public void processOperator(
final AbstractOperator op) {
if (op instanceof BinaryOperator) {
if ((lastOperator == null) || (data.isLastOpEquals())) {
data.setBuffer(Double.parseDouble(data.getDisplay()));
data.setInitDisplay(true);
} else {
lastOperator.operate(data);
}
lastOperator = op;
} else if (op instanceof UnaryOperator) {
op.operate(data);
}
data.setLastOpEquals(false);
}

public void processDigit(final String s) {
if (data.isLastOpEquals()) {
lastOperator = null;
}
if (data.isInitDisplay()) {
if (data.isLastOpEquals()) {
data.setBuffer(0.0);
} else {
data.setBuffer(Double.parseDouble(data.getDisplay()));
}
data.setDisplay(s);
data.setInitDisplay(false);
} else {
if (data.getDisplay().indexOf(
CalculatorConstants.POINT) == -1) {
data.setDisplay(data.getDisplay() + s);
} else if (!s.equals(CalculatorConstants.POINT)) {
data.setDisplay(data.getDisplay() + s);
}
}
data.setLastOpEquals(false);
}
}

CalculatorController создается с новым экземпляром CalculatorData. Наряду с элементом объекта данных у нашего контроллера также есть четыре основных действия: equals () и clear (), которые используются для операций калькулятора с одинаковыми именами, и processOperator () и processDigit (), которые используются, когда их соответствующие кнопки оператора нажимаются.

Действия контроллера, такие как нажатие цифры или оператора, обновляют модель, которая запускает соответствующие события. Когда данные в модели изменяются, в ответ на действия контроллера контролируются компоненты, зарегистрированные для прослушивания. Наше представление, CalculatorWidget, является одним из таких компонентов. Контроллер также содержит ссылку на lastOperator, чтобы он мог принимать решения о том, что делать в цепочке операций, которые он предоставляет. На рисунке 5 показан обзор классов, задействованных в нашем теперь завершенном проекте калькулятора с поддержкой MVC.

[Img_assist | NID = 4141 | название = | убывание = | ссылка = нет | Align = нет | ширина = 356 | Высота = 258]

Рисунок 5 Обзор классов, связанных с примером калькулятора GWT

Такое использование CalculatorController позволяет нашему клиентскому приложению, все его одиночество, обрабатывать состояние, логику, делегирование и использование отдельной модели вместе с представлением. Важно помнить это выражение шаблона MVC, и где лежат обязанности. Разработчики Java, привыкшие к циклу запрос-ответ, будут использовать представление, отображаемое в виде единого, обычно процедурного действия, основанного на модели одного состояния. В GWT, как и в хорошо разработанных настольных приложениях, модель может меняться независимо от отдельного слоя представления. Это заметно отличается от более переходного представления, которое выполняет одну операцию, а затем исчезает. Работа с состоянием представления, а не только с моделью, — это то, что вам необходимо учитывать при разработке.

Теперь у нас есть управляемый событиями GWT-калькулятор в форме повторно используемого CalculatorWidget. Кроме того, наш код достаточно устойчив к изменениям и расширяем, потому что мы использовали ОО-подход с операторами, ответственными за свои собственные отдельные операции.

Эта статья взята из главы 1 GWT на практике Роберта Купера и Чарли Коллинза и опубликована в мае 2008 года издательством Manning.