Sunday, January 29, 2012

Используем Listener вместо GWT Event Bus

При разработке GWT-приложений часто используют Event Bus для взаимодействия контроллеров. Такой вариант хорош, если на определенное событие должны реагировать несколько обработчиков. Если же взаимодействуют всего два контроллера, то использовать Event Bus затратно. В такой ситуации лучше подойдет шаблон проектирования Listener (он же - Observer).

Ранее мы создали приложение, использующее Event Bus. Создадим аналогичное, но с использованием шаблона Listener.

Файл pom.xml практически не требует изменений - только в названии приложения:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>listenerapp</groupId>
    <artifactId>listenerapp</artifactId>
    <version>1.0</version>
    <packaging>war</packaging>
    <name>listenerapp</name>

    <properties>
        <gwt.version>2.4.0</gwt.version>
        <maven-surefire-plugin.version>2.10</maven-surefire-plugin.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.google.gwt</groupId>
            <artifactId>gwt-servlet</artifactId>
            <version>${gwt.version}</version>
        </dependency>
        <dependency>
            <groupId>com.google.gwt</groupId>
            <artifactId>gwt-user</artifactId>
            <version>${gwt.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>gwt-maven-plugin</artifactId>
                <version>${gwt.version}</version>
                <configuration>
                    <draftCompile>true</draftCompile>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven-surefire-plugin.version}</version>
                <configuration>
                    <skipTests>true</skipTests>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>tomcat-maven-plugin</artifactId>
                <configuration>
                    <url>http://localhost:8080/manager</url>
                    <server>tomcat</server>
                    <path>/listenerapp</path>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Создадим интерфейс ClearListener в пакете com.listenerapp.client.view. Он содержит метод, который будет вызываться при нажатии на кнопку "Clear":
package com.listenerapp.client.view;

public interface ClearListener {

    void clear();
}

Кнопка "Clear" по-прежнему реализована в виде класса ButtonView в пакете com.listenerapp.client.view. Но вместо экземпляра SimpleEventBus, конструктор ButtonView принимает объект типа ClearListener. При нажатии на кнопку вызывается метод clear() экземпляра ClearListener:
package com.listenerapp.client.view;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.Button;

public class ButtonView extends Button {

    public ButtonView(final ClearListener clearListener) {
        setText("Clear");

        addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                clearListener.clear();
            }
        });
    }
}

Текстовое поле реализовано как класс TextBoxView в том же пакете. Класс содержит единственный метод clear(), который очищает содержимое поля:
package com.listenerapp.client.view;

import com.google.gwt.user.client.ui.TextBox;

public class TextBoxView extends TextBox {

    public void clear() {
        setText("");
    }
}

Создадим главный класс приложения - ListenerApp в пакете com.listenerapp.client. При создании экземпляра ButtonView укажем в качестве аргумента объект анонимного класса, реализующего интерфейс ClearListener. Анонимный класс реализует метод clear(), вызывая одноименный метод экземпляра TextBoxView. Таким образом, при нажатии на кнопку "Clear" объект типа ButtonView будет вызывать метод clear() объекта типа TextBoxView, хотя оба класса не знают о существовании друг друга:
package com.listenerapp.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.RootPanel;
import com.listenerapp.client.view.ButtonView;
import com.listenerapp.client.view.ClearListener;
import com.listenerapp.client.view.TextBoxView;

public class ListenerApp implements EntryPoint {
    private ButtonView buttonView;
    private TextBoxView textBoxView;

    @Override
    public void onModuleLoad() {
        textBoxView = new TextBoxView();

        buttonView = new ButtonView(new ClearListener() {
            @Override
            public void clear() {
                textBoxView.clear();
            }
        });

        RootPanel.get().add(buttonView);
        RootPanel.get().add(textBoxView);
    }
}

Файл настройки ListenerApp.gwt.xml аналогичен файлу настройки EventBusApp.gwt.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 1.6.2//EN"
        "http://google-web-toolkit.googlecode.com/svn/tags/1.6.2/distro-source/core/src/gwt-module.dtd">

<module rename-to='listenerapp'>
    <inherits name='com.google.gwt.user.User'/>
    <inherits name='com.google.gwt.user.theme.standard.Standard'/>

    <entry-point class='com.listenerapp.client.ListenerApp'/>

    <set-property name="user.agent" value="safari"/>
</module>

В файле web.xml изменим стартовую страницу приложения:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
        PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>

    <welcome-file-list>
        <welcome-file>ListenerApp.html</welcome-file>
    </welcome-file-list>

</web-app>

Страница ListenerApp.html аналогична EventBusApp.html:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>
<head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <title>ListenerApp</title>
    <script type="text/javascript" language="javascript"
            src="listenerapp/listenerapp.nocache.js"></script>
</head>
<body>

</body>
</html>

Запустим Tomcat, соберем и развернем приложение, выполнив команду "mvn clean tomcat:deploy":
[INFO] Scanning for projects...
[WARNING] 
[WARNING] Some problems were encountered while building the effective model for listenerapp:listenerapp:war:1.0
[WARNING] 'build.plugins.plugin.version' for org.codehaus.mojo:tomcat-maven-plugin is missing. @ line 56, column 21
[WARNING] 
[WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.
[WARNING] 
[WARNING] For this reason, future Maven versions might no longer support building such malformed projects.
[WARNING] 
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building listenerapp 1.0
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:2.4.1:clean (default-clean) @ listenerapp ---
[INFO] Deleting D:\Work\listenerapp\target
[INFO] 
[INFO] >>> tomcat-maven-plugin:1.1:deploy (default-cli) @ listenerapp >>>
[INFO] 
[INFO] --- maven-resources-plugin:2.4.3:resources (default-resources) @ listenerapp ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory D:\Work\listenerapp\src\main\resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ listenerapp ---
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 4 source files to D:\Work\listenerapp\target\classes
[INFO] 
[INFO] --- maven-resources-plugin:2.4.3:testResources (default-testResources) @ listenerapp ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory D:\Work\listenerapp\src\test\resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:testCompile (default-testCompile) @ listenerapp ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-surefire-plugin:2.10:test (default-test) @ listenerapp ---
[INFO] Tests are skipped.
[INFO] 
[INFO] --- gwt-maven-plugin:2.4.0:compile (default) @ listenerapp ---
[INFO] auto discovered modules [com.listenerapp.ListenerApp]
[INFO] Compiling module com.listenerapp.ListenerApp
[INFO]    Compiling 1 permutation
[INFO]       Compiling permutation 0...
[INFO]    Compile of permutations succeeded
[INFO] Linking into D:\Work\listenerapp\target\listenerapp-1.0\listenerapp
[INFO]    Link succeeded
[INFO]    Compilation succeeded -- 20.720s
[INFO] 
[INFO] --- maven-war-plugin:2.1.1:war (default-war) @ listenerapp ---
[INFO] Packaging webapp
[INFO] Assembling webapp [listenerapp] in [D:\Work\listenerapp\target\listenerapp-1.0]
[INFO] Processing war project
[INFO] Copying webapp resources [D:\Work\listenerapp\src\main\webapp]
[INFO] Webapp assembled in [141 msecs]
[INFO] Building war: D:\Work\listenerapp\target\listenerapp-1.0.war
[WARNING] Warning: selected war files include a WEB-INF/web.xml which will be ignored 
(webxml attribute is missing from war task, or ignoreWebxml attribute is specified as 'true')
[INFO] 
[INFO] <<< tomcat-maven-plugin:1.1:deploy (default-cli) @ listenerapp <<<
[INFO] 
[INFO] --- tomcat-maven-plugin:1.1:deploy (default-cli) @ listenerapp ---
[INFO] Deploying war to http://localhost:8080/listenerapp  
[INFO] OK - Deployed application at context path /listenerapp
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 27.346s
[INFO] Finished at: Sun Jan 29 23:01:31 EET 2012
[INFO] Final Memory: 15M/309M
[INFO] ------------------------------------------------------------------------

Перейдём по ссылке http://localhost:8080/listenerapp/. Должна отображаться страница с кнопкой "Clear" и текстовым полем. При нажатии на кнопку содержимое поля должно очищаться:











Структура проекта:




























Исходный код созданного приложения доступен по ссылке https://code.google.com/p/listenerapp/.

2 comments:

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

    ReplyDelete