演示者(包含逻辑)和视图(UI 控件的愚蠢包装)之间的分离允许您:
- 为演示者编写不需要相应环境(桌面、浏览器、GWT 小部件)即可运行的单元测试
- 重用前端逻辑,而无需绑定到一组特定的小部件/UI 框架
后一种用例很少见,因此让我们重点关注 MVP 模型对自动化、程序化测试的适用性。对于开发团队来说,这通常采取连续构建/测试周期的形式,使用Hudson http://hudson-ci.org/(或类似)在无头服务器上,每次运行测试时打开 Web 浏览器、创建控件等是不切实际的。
MVP+GWT 的典型用法是视图实现呈现器提供的接口,并且通常该接口是根据其他通用接口定义的。这是一个非常简单的演示者,当单击按钮时,它会增加数字标签 - 请注意,而不是公开TextBox http://google-web-toolkit.googlecode.com/svn/javadoc/2.0/com/google/gwt/user/client/ui/TextBox.html and Button http://google-web-toolkit.googlecode.com/svn/javadoc/2.0/com/google/gwt/user/client/ui/Button.html直接,视图返回通用的HasText http://google-web-toolkit.googlecode.com/svn/javadoc/2.0/com/google/gwt/user/client/ui/HasText.html and 有点击处理程序 http://google-web-toolkit.googlecode.com/svn/javadoc/2.0/com/google/gwt/event/dom/client/HasClickHandlers.html实例:
public class ButtonClickPresenter {
public interface View {
HasText currentValue();
HasClickHandlers incrementButton();
}
private final View myView;
private int currentValue = 0;
public ButtonClickPresenter(View myView) {
this.myView = myView;
this.myView.currentValue().setText("0");
this.bind(); // for the sake of demonstration
}
public void bind() {
this.myView.incrementButton.addClickHandler(
@Override
new ClickHandler() {
void onClick(ClickEvent event) {
currentValue ++;
myView.currentValue().setText(
Integer.toString(currentValue));
}
});
}
}
“真实”视图返回 UI 小部件(在本例中通过 UiBinder 创建):
public class ButtonClickView implements ButtonClickPresenter.View {
// ... skipped UiBinder initialisation ...
@UiField Label currentValueLabel;
@UiField Button incrementButton;
@Override
public HasText currentValue() {
return currentValueLabel;
}
@Override
public HasClickHandlers incrementButton() {
return incrementButton;
}
// ... etc ...
}
而单元测试创建一个虚拟实现(或使用Mockito http://code.google.com/p/mockito/, EasyMock http://easymock.org/等),因此不需要任何 UI 组件:
public class ButtonClickPresenterTest {
ButtonClickPresenter presenter;
ClickHandler currentHandler;
String currentText;
@Before
public void setUp() {
presenter = new ButtonClickPresenter(
// dummy view - just stores label text in a String and
// keeps track of the Presenter's click handler
new ButtonClickPresenter.View() {
@Override
public HasText currentValue() {
return new HasText() {
@Override public String getText() { return currentText; }
@Override public void setText(String text) { currentText = text; }
};
}
@Override
public HasClickHandlers incrementButton() {
return new HasClickHandlers() {
@Override
public HandlerRegistration addClickHandler(ClickHandler handler) {
currentHandler = handler;
}
};
}
});
}
@Test
public void testIncrement() {
// initial value
assertEquals("0", currentText);
// clicking the button should increment the number
currentHandler.onClick(null);
assertEquals("1", currentText);
}
}
至于你的下一段:你的视图根本不应该连接到数据库!演示者应通过 Service/ServiceAsync(或诸如此类的抽象)请求数据gwt-调度 http://code.google.com/p/gwt-dispatch/ or gwt-平台 http://code.google.com/p/gwt-platform/),然后调用视图上的方法来填充 UI。
顺便说一下,最后两个链接(以及gwt-演示者 http://code.google.com/p/gwt-presenter/)如果您正在寻找 GWT MVP 代码示例 - 结合谷歌杜松子酒 http://code.google.com/p/google-gin/他们提供了将所有这些东西结合在一起的框架。
说了这么多,我同意——GWT+MVP+Java 的组合可以是艰苦的工作,极其冗长(我很高兴这些天我不必经常使用它)。然而,另一种选择则更没有吸引力:一个无法测试、无法维护的意大利面球……