【问题标题】:using GIN in GWT Activities在 GWT 活动中使用 GIN
【发布时间】:2012-03-29 11:45:32
【问题描述】:
我的每个活动都需要一个相应的单例视图实现。将它们注入活动的最佳策略是什么?
构造函数注入 Activity 构造函数是从 ActivityMapper 的 getActivity() 调用的。 ctor 已经有一个参数(一个 Place 对象)。我必须创建 ActivityMapper 并注入所有可能的视图。不好...
-
方法注入 - “这样注释的函数会在构造函数执行后自动执行。”(GWT in Action,第 2 版)好吧, “在执行ctor之后”显然不够快,因为当调用Activity的start()方法并且我得到一个NPE时,视图(或以这种方式注入的RPC服务)仍未初始化。
李>
在 Activity 的 ctor 中使用 GWT.create 构造注入器。没用,因为它们不再是单例了。
【问题讨论】:
标签:
gwt
dependency-injection
gwt-gin
gwt-activities
【解决方案1】:
最适合我们的是使用辅助注射。
根据情况,我们在活动本身、包中(用于构建该包中的所有活动)或 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 对象直接传递给活动。尝试解耦地点和活动,这允许您移动事物(例如,构建移动或平板电脑版本,您可以在主视图和详细视图之间切换,而不是并排显示它们)只需更改“外壳”布局和您的活动映射器。要真正解耦它们,您必须构建某种类型的 navigator,它会抽象您的 placeController.goTo() 调用,以便您的活动永远不会处理地点。
【解决方案2】:
我选择了一种稍微不同的方法,它具有您需要的所有灵活性。我不记得我在哪里选择了这个设计模式,但这不是我的主意。我这样创建活动
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 模块文件中声明为单例。
【解决方案3】:
根据我的经验,一个好的做法是使用单独的活动映射器来处理地点和活动(映射)。在您有演示者的活动中,这是一个活动的示例:
public class ActivityOne extends AbstractActivity {
@Inject
private Presenter presenter;
@Override
public void start(AcceptsOneWidget panel, EventBus eventBus) {
presenter.go(panel);
}
}
presenter 内部注入了视图,它是在调用“go”方法时构造的(presenter)。演示者在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();
}
}
最后我必须指出,有一些活动,例如菜单的活动,它直接处理地点和视图以显示当前状态。这些活动被缓存在映射器中,以避免每次更改位置时都出现新实例。