经典MVP
您知道如何在Facebook中发布链接吗? –最近,我不得不为一个小GWT旅行应用程序创建此功能。
因此,您可以输入一个URL,然后将其提取并解析。 您可以从页面中选择图像之一,查看文本,最后存储链接。
现在如何在MVP中正确设置此设置? –首先,创建一个类似于视图的抽象接口:
interface Display {
HasValue<String> getUrl();
void showResult();
HasValue<String> getName();
HasClickHandlers getPrevImage();
HasClickHandlers getNextImage();
void setImageUrl(String url);
HasHTML getText();
HasClickHandlers getSave();
}
它利用GWT组件实现的接口来提供对其状态和功能的某些访问。 在测试期间,您无需参考GWT内部即可轻松实现此接口。 同样,可以在不影响更深层逻辑的情况下更改视图实现。
该实现非常简单,此处显示了声明的UI字段:
class LinkView implements Display
@UiField TextBox url;
@UiField Label name;
@UiField VerticalPanel result;
@UiField Anchor prevImage;
@UiField Anchor nextImage;
@UiField Image image;
@UiField HTML text;
@UiField Button save;
public HasValue<String> getUrl() {
return url;
}
public void showResult() {
result.setVisible(true);
}
// ... and so on ...
}
然后,演示者使用接口访问视图,按照惯例,该接口写在presenter类中:
class LinkPresenter
interface Display {...};
public LinkPresenter(final Display display) {
display.getUrl().addValueChangeHandler(new ValueChangeHandler<String>() {
@Override
public void onValueChange(ValueChangeEvent<String> event) {
Page page = parseLink(display.getUrl().getValue());
display.getName().setValue(page.getTitle());
// ...
display.showResult();
}
});
}
// ... and so on ...
}
因此,我们在这里:使用MVP,您可以很好地构造代码并使代码易于阅读。
简化
收益是: 每个屏幕或组件三种类型 。 每当重新定义UI时,三个文件都将更改。 未将ui.xml文件计入视图声明。 对于像我这样的懒人来说,这些太多了。 而且,如果您查看视图实现,很明显如何简化它:
使用视图声明(* .ui.xml)作为视图,并将ui元素直接注入到presenter中:
class LinkPresenter
@UiField HasValue<String> url;
@UiField HasValue<String> name;
@UiField VerticalPanel result;
@UiField HasClickHandlers prevImage;
@UiField HasClickHandlers nextImage;
@UiField HasUrl image;
@UiField HasHTML text;
@UiField HasClickHandlers save;
public LinkPresenter(final Display display) {
url.addValueChangeHandler(new ValueChangeHandler<String>() {
@Override
public void onValueChange(ValueChangeEvent<String> event) {
Page page = parseLink(url.getValue());
name.setValue(page.getTitle());
// ...
result.setVisible(true);
}
});
}
// ... and so on ...
}
由于可以使用它们的接口声明注入的元素,因此此演示者具有成熟的MVP演示者的许多优点:您可以通过设置实现组件来对其进行测试(请参见下文),并且可以轻松地更改视图实现。
但是现在,您将所有这些都放在一个类和一个view.ui.xml文件中,并且可以更简单地应用结构更改。
使UI元素抽象
TextBox实现HasValue <String>。 这很简单。 但是,不能通过接口访问的ui元素的属性呢? 您可能已经认识到的一个示例是上述代码中的VerticalPanel命名结果result及其方法setVisible(),不幸的是,该方法在UiObject基类中实现。 因此,没有可用的接口,例如。 在测试时实施。 为了能够切换视图实现,最好注入ComplexPanel,但即使在测试时也无法实例化。
例如,在这种情况下,唯一的解决方法是创建一个新接口
interface Visible {
void setVisible(boolean visible);
boolean isVisible();
}
和子类化有趣的UI组件,实现相关的接口:
package de.joergviola.gwt.tools;
class VisibleVerticalPanel
extends VerticalPanel
implements Visible {}
这似乎是乏味和次优的。 但是,只能像上述成熟的MVP中那样仅针对每个组件而不是针对每个视图进行操作。
等待-如何在UiBuilder模板中使用自制组件? –很简单:
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g="urn:import:com.google.gwt.user.client.ui"
xmlns:t="urn:import:de.joergviola.gwt.tools">
<g:VerticalPanel width="100%">
<g:TextBox styleName="big" ui:field="url" width="90%"/>
<t:VisibleVerticalPanel ui:field="result"
visible="false" width="100%">
</t:VisibleVerticalPanel>
</g:VerticalPanel>
</ui:UiBinder>
声明处理程序
声明(click-)处理程序的标准方法非常方便:
@UiHandler("login")
public void login(ClickEvent event) {
srv.login(username.getValue(), password.getValue());
}
在简化的MVP方法中,此代码将驻留在演示者中。 但是ClickEvent参数是View组件,可以例如。 不能在运行时实例化。 另一方面,由于UiBuilder需要Event参数,因此无法从签名中将其删除。
因此,不幸的是,您必须坚持手动注册ClickHandlers(因为无论如何都必须执行完整的MVP):
public initWidget() {
...
login.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
login();
}
});
...
}
public void login(ClickEvent event) {
srv.login(username.getValue(), password.getValue());
}
测试中
引入MVP时,使您的应用程序可测试是主要目标之一。
GwtTestCase能够在容器环境中执行测试,但需要一些启动时间。 在TDD中,希望有一个运行速度非常快的测试,可以在每次更改后应用它而不会丢失上下文。 因此,MVP旨在能够在标准JVM中测试所有代码。 在标准MVP中,您将创建视图接口的实现。 在这种简化方法中,只需在组件接口级别上创建实现即可,如下所示:
class Value<T> implements HasValue<T> {
private T value;
List<ValueChangeHandler<T>> handlers =
new ArrayList<ValueChangeHandler<T>>();
@Override
public HandlerRegistration addValueChangeHandler(
ValueChangeHandler<T> handler) {
handlers.add(handler);
return null;
}
@Override
public void fireEvent(GwtEvent<?> event) {
for (ValueChangeHandler<T> handler : handlers) {
handler.onValueChange((ValueChangeEvent) event);
}
}
@Override
public T getValue() {
return value;
}
@Override
public void setValue(T value) {
this.value = value;
}
@Override
public void setValue(T value, boolean fireEvents) {
if (fireEvents)
ValueChangeEvent.fire(this, value);
setValue(value);
}
}
与往常一样,您必须将此组件注入到被测演示者中。 尽管从原则上讲,您可以为组件创建一个设置器,但我仍然遵循通常的技巧来使组件受到包保护,将测试与演示者放入同一包中(但当然是不同的项目文件夹),然后直接设置组件。
你赢了什么?
您得到的代码结构像完整的MVP一样干净,只需要更少的类和样板代码。
在某些情况下,组件及其接口需要实用程序类,但是随着时间的流逝,您将构建一个真正易于理解,测试和扩展的环境。
我很好奇:告诉我您的经历!
参考: GWT MVP从我们的JCG合作伙伴那里变得简单 Joerg Viola在Joerg Viola博客上 。
翻译自: https://www.javacodegeeks.com/2012/02/gwt-mvp-made-simple.html