在GWT活动中使用GIN

时间:2012-03-29 11:45:32

标签: gwt dependency-injection gin gwt-activities

我的每个活动都需要一个对应的单一视图实现。将它们注入活动的最佳策略是什么?

  1. 构造函数注入从ActivityMapper的getActivity()调用Activity构造函数。 ctor已经有一个参数(一个Place对象)。我必须创建ActivityMapper,注入所有可能的视图。不好......

  2. 方法注入 - “在执行构造函数后自动执行注释的函数。”(GWT in Action,2nd Ed。)嗯, “在执行ctor之后”显然不够快,因为当调用Activity的start()方法并且我获得NPE时,视图(或以这种方式注入的RPC服务)仍​​未初始化。

  3. 在Activity的ctor中用GWT.create构造注入器。没用,因为他们不再是单身人士。

3 个答案:

答案 0 :(得分:7)

对我们最有效的方法是使用Assisted Inject。

根据具体情况,我们在活动本身,包中(用于构建该包中的所有活动)或ActivityMapper中定义了活动工厂。

public class MyActivity extends AbstractActivity {
   private final MyView view;

   @Inject
   MyActivity(MyView view, @Assisted MyPlace place) {
      this.view = view;
      ...
   }
   ...
}

public class MyActivityMapper implements ActivityMapper {
   public interface Factory {
     MyActivity my(MyPlace place);

     FooActivity foo(FooPlace place);

     ...
   }

   // using field injection here, feel free to replace by constructor injection
   @Inject
   private Factory factory;

   @Overrides
   public Activity getActivity(Place place) {
      if (place instance MyPlace) {
         return factory.my((MyPlace) place);
      } else if (place instance FooPlace) {
         return factory.foo((FooPlace) place);
      }
      ...
   }
}

// in the GinModule:
install(new GinFactoryModuleBuilder().build(MyActivityMapper.Factory.class));
顺便说一句,要使方法注入工作,你仍然需要通过GIN创建你的活动,所以你会遇到与构造函数注入相同的问题。没有魔法,GIN不会神奇地注入它不知道的类,甚至不知道它们何时被实例化。您可以通过向Ginjector添加方法来显式触发方法注入,但我不推荐它(您的代码将取决于Ginjector,如果可以,您应该避免这种情况):

interface MyGinjector extends Ginjector {
   // This will construct a Foo instance and inject its constructors, fields and methods
   Foo foo();

   // This will inject methods and (non-final) fields of an existing Bar instance
   void whatever(Bar bar);
}

...

Bar bar = new Bar("some", "arguments");
myGinjector.whatever(bar);
...

最后一句话:我不会将place对象直接传递给activity。尝试分离场所和活动,让你可以移动东西(例如,建立一个移动或平板电脑版本,你可以在主视图和详细视图之间切换,而不是并排显示它们)只需更改你的“shell”布局和你的活动地图。要真正解耦它们,你必须构建某种导航器,这将抽象你的placeController.goTo()调用,以便你的活动永远不会处理地方。

答案 1 :(得分:3)

我选择了一种略有不同的方法,它具有您所需的所有灵活性。我不记得我选择这个设计模式的地方,但这不是我的想法。我这样创建活动

public class MyActivity extends AbstractActivity{

    private MyView view;
    @Inject static PlaceController pc;


    @Inject
    public MyActivity(MyView view) {
        super();
        this.view = view;
    }

    public MyActivity withPlace(MyPlace myPlace) {
        return this;
    }
...
}

然后我在活动映射器中使用它,如下所示:

public class MyMapper implements ActivityMapper {

    @Inject Provider<MyActivity> myActivityProvider;

    public Activity getActivity(Place place) {

        if ( place instanceof MyPlace){
            return myActivityProvider.get().withPlace(place);
        } else if
...

还要确保在gin模块文件中将View声明为singleton。

答案 2 :(得分:2)

根据我的经验,一个好的做法是让单独的活动映射器来处理场所和活动(映射)。在您拥有演示者的活动中,以下是活动的示例:

public class ActivityOne extends AbstractActivity {

  @Inject
  private Presenter presenter;

  @Override
  public void start(AcceptsOneWidget panel, EventBus eventBus) {
    presenter.go(panel);
  }

}

演示者将视图注入内部,在调用“go”方法时构建(演示者)。演示者在GIN模块中声明为单例,视图通常是单例(有一些例外,例如在很多地方出现的小小部件)。

我们的想法是在展示者内部移动联系人视图(因为演示者的目标是根据MVP处理逻辑并从视图中检索/更新数据)。 在演示者内部,您还将拥有RPC服务,您无需声明它们,因为GIN将“神奇地”为您制作实例,方法是调用GWT.create 以下是一个简单的演示者示例:

    public class PresenterOneImpl implements Presenter {

      @Inject
      private MyView view;


      @Inject
      private SomeRpcServiceAsync someRpc;


      @Override
      public void go(AcceptsOneWidget panel) {
        view.setPresenter(this);
        panel.setWidget(view);
        updateTheViewWithData();
      }
}

最后我必须注意,有一些活动,比如菜单的活动,它们直接处理场所和视图以显示当前状态。这些活动缓存在映射器内,以避免每次更改位置时都有新实例。