GWT MVP变得简单

GWT Model-View-Presenter是用于大规模应用程序开发的设计模式。 它源于MVC,它在视图和逻辑之间进行划分,并有助于创建结构良好,易于测试的代码。 为了帮助像我这样的懒惰开发人员,我研究了如何减少使用声明式UI时要编写的类和接口的数量。

经典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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值