2
votes

I've started a GWT project, I've decided to give the UiBinder a try. I'm having a difficult time laying an MVP pattern on top of UiBinder.

When I was using GWT-pure-java: I'd use Gin to inject my presenter with the corresponding view. It was pretty straight forward, and if I wanted to pass an id into the presenter, then I would simply pass an id into the presenter's constructor.

Not so straight forward with UiBinder. I'm almost certain that I'm missing something, because lots of people are claiming that UiBinder and MVP are a match made in heaven...so I'm hoping to get some solid responses on this query ;-)

What I've seen in several trivial GWT-UiBinder examples, is that the view is created by the binder then either:

  1. The view constructs the presenter either in its constructor or via a @UIFactory method.
  2. The corresponding presenter is passed to the view (via a setter, needless to say after the view is constructed).

With the first approach, how does one pass an id to the presenter if the presenter is being constructed in the view? Would you do view.getPresenter().setId(42);, and then the presenter would go to the server get some info and ask the view to display it...smells bad.

With the second approach, one would end up with a non-intuitive object graph in which it is not clear who is the consumer and who is the producer. Also, in situations where the view requires information from the presenter (almost all use-cases require this) what would one do:

//some code would create the presenter pass it the id and then call view.setPresenter
class MyView {
    void setPresenter(MyPresenter p) {
    this.presenter = p;
    //OK now that i have my presenter how do I ask it to fetch data from the server.
    //do i turn around and do: presenter.setView(this); and then the presenter takes 
    //over and uses the view to display the data?
     }
 }

This is equally smelly...Sorry for the long post, and thanks in advance...

2

2 Answers

6
votes

You're right in that it does seem a bit unclean to have both a View reference its Presenter, and have the presenter reference the View.

The way I see it, and how the google dev pages on MVP outline there are two flavours of MVP:

  1. Have a view that doesn't know about its presenter, and "wrap" it with a presenter. The view provides a sufficient API for the presenter to get/set all the data it cares about. Additionally the presenter must know about all event types the view can generate, in order to respond to user interaction. This is the method of MPV part 1.
  2. The presenter once more knows about the view, but this time only in a get/set data capacity. The presenter does not concern itself with the UI events in the view. Instead, the presenter provides an API for the view to call/notify it when events happen in the form of "onSomethingHappened()" methods in our presenter. This allows us to capture all behaviour/logic in the presenter itself, and invoke it as necessary when something happens in the view. The view can then handle events at a very low level in any way suitable - whether using GWT events/widgets or raw DOM events with elements/HTML (UiBinder/GWT performance best practices). This is the method of MVP part 2.

I prefer option 2 as it allows the presenter to be purely focused on the necessary behaviour. The view can deal with widgets/html/event handling as necessary and simplify it to calls to "onSomething()" calls for the presenter. Those widget/event implementations might be simple or complex and optimized events. The presenter is unaffected (and unpolluted) by the detail, as it just gets notified. I feel this option is a cleaner separation of presentation and behaviour. Note it's also a 1 to 1 implementation of the Observer Pattern, so the interconnection between View and Presenter is necessary.

As for creation, I feel the Presenter is the stronger entity despite it playing the Observer role. I would create the necessary presenter and then pass it the view of concern. The presenter can then take control of the view, and pass the view a self reference.

As for your producer/consumer analogy I think the presenter is the consumer. The view produces UI events (user interaction) and the presenter responds by providing the necessary behaviour. That should be the only point of contact between the view and presenter - the view calls methods like "onSomethingHappened()" and the presenter does the work. The view would never tell the presenter "fetchData()" or anything like that.

I've just started using UiBinder + MVP recently myself, so this is just what I think. I hope it helps!

0
votes

It has been a few days since I started this thread and read the responses. I've decided on my approach. And I'd thought I just mention it here.

Once again, thanks for the thoughtful responses...they were helpful.

As filip-fku pointed out presenters are the main entity, so I decided to continue to threat them as such during the life-cycle management of my mvp objects. In other words, the views do not instantiate the presenters. The presenters are instantiated by other presenters (at some point i'll delegate to Gin).

The builder of a presenter has access to its corresponding view, and injects the presenter (via constructor) with the view.

The views themselves are either instantiated by the builder or instantiated by UiBinder as part of a larger view. In the latter case where UiBinder instantiates the view the parent view has a getter. Here is an example of this case:

/*pojo for the parent ui-binder*/
public class Form implements FormPresenter.View {
@UiField PromptView namePrompt;
@override
PromptPresenter.View getNamePromptDisplay() {
    return namePrompt;  //introduced into this pojo via @UiField
}
//bunch of view code
}//end of the class

Then in the FormPresenter I do:

private void buildNamePrompt() {
  new PromptPresenter(display.getNamePromptDisplay(), etc....);
}

I have essentially kept the mvp life-cycle similar to the pure java approach. Once I get some milage out of this, I'll refactor it with Gin.

Thanks again. P.S. if you haven't seen the i/o presentation mentioned above, it is worth checking out.