gwt的mvp模式
经典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 LinkPresenterinterface Display {...};public LinkPresenter(final Display display) {display.getUrl().addValueChangeHandler(new ValueChangeHandler<String>() {@Overridepublic 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>() {@Overridepublic 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() {@Overridepublic 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>>();@Overridepublic HandlerRegistration addValueChangeHandler(ValueChangeHandler<T> handler) {handlers.add(handler);return null;}@Overridepublic void fireEvent(GwtEvent<?> event) {for (ValueChangeHandler<T> handler : handlers) {handler.onValueChange((ValueChangeEvent) event);}}@Overridepublic T getValue() {return value;}@Overridepublic void setValue(T value) {this.value = value;}@Overridepublic void setValue(T value, boolean fireEvents) {if (fireEvents)ValueChangeEvent.fire(this, value);setValue(value);}}
与往常一样,您必须将此组件注入到被测演示者中。 尽管从原则上讲,您可以为组件创建一个设置器,但我仍然遵循通常的技巧来使组件受包保护,将测试与演示者放入同一包中(但当然是不同的项目文件夹),然后直接设置组件。
你赢了什么?
您得到的代码结构像完整的MVP一样干净,只需要少得多的类和样板代码。
在某些情况下,组件及其接口需要实用程序类,但是随着时间的流逝,您将构建一个真正易于理解,测试和扩展的环境。
我很好奇:告诉我您的经历!
参考: JWT合作伙伴 简化了GWT MVP Joerg Viola在Joerg Viola博客上 。
翻译自: https://www.javacodegeeks.com/2012/02/gwt-mvp-made-simple.html
gwt的mvp模式