我的每个活动都需要一个对应的单一视图实现。将它们注入活动的最佳策略是什么?
构造函数注入从ActivityMapper的getActivity()调用Activity构造函数。 ctor已经有一个参数(一个Place对象)。我必须创建ActivityMapper,注入所有可能的视图。不好......
方法注入 - “在执行构造函数后自动执行注释的函数。”(GWT in Action,2nd Ed。)嗯, “在执行ctor之后”显然不够快,因为当调用Activity的start()
方法并且我获得NPE时,视图(或以这种方式注入的RPC服务)仍未初始化。
在Activity的ctor中用GWT.create构造注入器。没用,因为他们不再是单身人士。
答案 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();
}
}
最后我必须注意,有一些活动,比如菜单的活动,它们直接处理场所和视图以显示当前状态。这些活动缓存在映射器内,以避免每次更改位置时都有新实例。