MVP framework

192 views
Skip to first unread message

saurabh saurabh

unread,
Feb 3, 2012, 5:10:07 AM2/3/12
to Google Web Toolkit
Hi everyone,
there are lots of MVP frameworks out there like
MVP4G, Guit etc and also google's official 'Places and Activities'. I
wanted to know which is most popular framework or way of MVP
implementation in industry working with GWT.

Thanks in advance

Drew Spencer

unread,
Feb 3, 2012, 7:47:33 AM2/3/12
to google-we...@googlegroups.com
I had this problem for ages. I think for a beginner you are best learning to do it from scratch as described here:  http://code.google.com/webtoolkit/articles/mvp-architecture.html

It's a lot to get your head around, it took me weeks and weeks to really get what was going on.

Everyone has their preference though.

Regards,

Drew

dparish

unread,
Feb 6, 2012, 9:55:34 PM2/6/12
to google-we...@googlegroups.com
I recently finished an example app with MVP, Activities and Places, Gin and Guice, Request Factory and a few other GWT components.  Feel free to grab the source and see if it gets you started.

Thomas Broyer

unread,
Feb 7, 2012, 6:23:21 AM2/7/12
to google-we...@googlegroups.com
Looks great!

A few notes:
  • You should use DI as most as possible, e.g. inject Provider<>s in the AppActivityMapper, and inject the PlaceController into the activities, instead of using SchoolPlanner.getInjector(). Actually, your injector instance shouldn't be stored in a 'static' field and shouldn't ever be used outside your EntryPoint; simply GWT.create() it within the onModuleLoad, it'll work just as well and will remove the incentive to call it from elsewhere rather than having your dependencies injected. Your Ginjector would then also only have to declare accessors for the PlaceHistoryMapper (or *Handler, depending on how you do it; I prefer creating the Handler and calling register() in my onModuleLoad) and ActivityMapper-s (or *Manager-s).
  • The ActivityManager and PlaceHistoryHandler don't have to be @Singleton: you'll only ever use them once; same for the default place. You should also use binding annotations, this will help when you'll add other ActivityManager-s (and this is why I prefer surfacing my ActivityMapper-s, with their specific classes, that way I don't even need binding annotations to distinguish them). See http://www.wanderingcanadian.ca/a-simpler-way-to-use-gin-in-multi-display-gwt
  • Why are you injecting the EventBus in your activities? There's almost no reason to not use the one passed to the start() method.
  • In the AppActivityMapper, why are you "creating an empty student"? Why aren't you simply passing the ID to the activity? (instead of an entity). Your student.getEmail() checked are fragile, they remind me of browser-sniffing vs. feature-detection.
  • In your activities, you should call setPresenter on the view only on start(). Calling it in the constructor means the new activity is "stealing" the view from the previous not-yet-stopped one, which could do things with the view in its onStop(). Well, it's safer to only setPresenter in start(). What we did was to also setPresenter(null) in onStop and onCancel, and in the view's setPresenter add an « assert this.presenter != null && presenter == null : "someone's trying to steal the view from another presenter"; » (or something similar)
  • You should check whether the activity is still "active" before calling PlaceController#goTo from your Receiver: the user might have navigated away from the activity already.
  • You don't want to inject a RequestContext, this is something that should be entirely controlled (including most importantly its lifetime) by the "user code". Inject the RequestFactory or a Provider<> instead (injecting the RequestFactory induces some coupling, but makes it clearer the method will always *create* a new instance, whereas with a provider you defer the scope handling to the injector)
  • IMO, you should only "push" from your presenter to the view, or from the view to the presenter. Your views' render() methods "pull" from their presenter. Also, these render() methods should rather live in the presenter IMO, and/or possibly use the Editor framework (with the EditorDriver "driven" by the presenter)
  • Your entities should IMO live in "server" (no "shared") and your proxies in "shared" (not "client"): the server needs the proxies, and the client doesn't need the entities (it only deals with the proxies)

dparish

unread,
Feb 7, 2012, 10:28:19 AM2/7/12
to google-we...@googlegroups.com
Great tips.  I'll take a look at each points. If I find I have a reason not to implement one of the comments I'll let you know.  

John99

unread,
Feb 8, 2012, 6:37:28 AM2/8/12
to google-we...@googlegroups.com
Perfect tips Thomas. Thank you!
Have a question about this item:

You should check whether the activity is still "active" before calling PlaceController#goTo from your Receiver: the user might have navigated away from the activity already. 

How we can check it?           placeController.getWhere() ?

dparish

unread,
Feb 8, 2012, 7:39:10 PM2/8/12
to google-we...@googlegroups.com
Thomas,

I'm working on restructuring AppActivityMapper so that I inject in the Activities.  My reasoning for the static injector was it kept me from having gigantic constructor arguments.  If AppActivityMapper has 30 activities, that's alot of constructor arguments.  

MyApp.getInjector().getActivityFoo()  seemed like a very clean way to get around that.  I get that it breaks injection though.  

I could pass in the injector but I suspect that's bad form as well ;)

I could have injected fields, but that's pretty heavily frowned upon, but it sure makes for more readable code.  Any options?  How about a Provider that can provide mutliple classes.

Something like
Provider<Activity> parentProvider
that could return  child classes?  From reading the Guice docs that doesn't seem it would work.


Thomas Broyer

unread,
Feb 9, 2012, 5:01:15 AM2/9/12
to google-we...@googlegroups.com
You could simply use a boolean field updated by start(), onStop() and onCancel(). In our case, we store the AcceptsOneWidget in a field on start() and set the field to 'null' in onStop() and onCancel().

Thomas Broyer

unread,
Feb 9, 2012, 5:08:37 AM2/9/12
to google-we...@googlegroups.com


On Thursday, February 9, 2012 1:39:10 AM UTC+1, dparish wrote:
Thomas,

I'm working on restructuring AppActivityMapper so that I inject in the Activities.  My reasoning for the static injector was it kept me from having gigantic constructor arguments.  If AppActivityMapper has 30 activities, that's alot of constructor arguments.

I also prefer constructor injection, but in the case of ActivityMapper-s, which have a lot of dependencies, I make an exception and simply use field injection:

class AppActivityMapper implements ActivityMapper {
   @Inject Provider<FooActivity> fooActivityProvider;
   @Inject Provider<BarActivity> barActivityProvider;
   …
 
MyApp.getInjector().getActivityFoo()  seemed like a very clean way to get around that.  I get that it breaks injection though.

What's the difference between declaring 30 dependencies in AppActivityMapper and declaring 30 accessor methods in the Ginjector?

In our case, we actually make an heavy use of assited-inject, so we declare factory interfaces within the AppActivityMapper and simply inject that factory (I generally prefer declaring an inner Factory interface in the type that's being constructed, but we make an exception for activities and use a single –or only a few– factory that can provide many activity types, and declare it within the ActivityMapper).
Of course, YMMV.

I could pass in the injector but I suspect that's bad form as well ;)

Would still be better than accessing a static field (you could possibly mock the Ginjector if you wanted to unit-test your ActivityMapper)
 
Reply all
Reply to author
Forward
0 new messages