經典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一樣干凈,只需要更少的類和樣板代碼。
在某些情況下,組件及其接口需要實用程序類,但是隨著時間的流逝,您將構建一個真正易于理解,測試和擴展的環境。
我很好奇:告訴我您的經歷!
參考: GWT MVP從我們的JCG合作伙伴那里變得簡單 ? Joerg Viola在Joerg Viola博客上 。
翻譯自: https://www.javacodegeeks.com/2012/02/gwt-mvp-made-simple.html