Reversing the MVP pattern and using @UiHandler

EDIT: This post has been slightly edited to follow the new naming convention used in Gwt-Platform.

Through usage, we have found that the second MVP pattern proposed by Google could save us a lot of tedious anonymous classes. This approach mainly consists of creating an interface that your presenter implements and which lets your view access some of its methods. This technique can be readily used with Gwt-Platform, but it is lacking documentation. So now is the time to explain it properly!

The first thing you’ll notice is that the latest version of GWTP introduces some interfaces and abstract classes that makes it easy for you to use this pattern:

  • The HasUiHandlers interface is used to indicate that your view participates in the pattern. A view that implements this interface needs to have uiHandlers, that is, a set of methods it can use to initiate complex actions in the presenter.
  • UiHandlers is a marker interface used to define your controls, you inherit from it and define all the methods your view will need to use.
  • The abstract class ViewWithUiHandlers inherits from ViewImpl and is the straightforward implementation of HasUiHandlers. Your view can inherit from it to save you some typing.
  • The abstract class PopupViewWithUiHandlers is the same as the precedent but inherits from PopupViewImpl. Use it for your dialogs that participate in the pattern.

Now don’t be confused by all these tools. You’ll see that using the pattern is quite straightforward. First you need to create an interface that extends UiHandlers and add all the methods your view needs to call. We usually call that interface MyUiHandlers and place it inside the view to keep things organized:

[sourcecode language=”java”]
public interface MyUiHandlers extends UiHandlers {
void onSave();
}
[/sourcecode]

Your presenter then needs to implement this interface:

[sourcecode language=”java”]
public class ExamplePresenter extends Presenter<ExamplePresenter.MyView, ExamplePresenter.MyProxy>
implements MyUiHandlers {

@Override
public void onSave() {
doSomething();
}

}
[/sourcecode]

Then you have to connect these controls to your view. This is done by letting MyView extend HasUiHandlers and by calling setUiHandlers(impl) within your presenter’s constructor to finalize the connection:

[sourcecode language=”java”]
public class ExamplePresenter extends Presenter<ExamplePresenter.MyView, ExamplePresenter.MyProxy> implements MyUiHandlers {
public interface MyView extends View, HasUiHandlers {
}
@Inject
ExamplePresenter(final EventBus eventBus, final MyView view, final MyProxy proxy) {
super(eventBus, view, proxy);
getView().setUiHandlers(this);
}

}
[/sourcecode]

Be careful: since the view is instantiated before the presenter, the setUiHandlers(impl) method will be called after the view’s constructor has executed. This means you cannot refer to the presenter within your view’s constructor. Also, it’s important to call setUiHandlers() early, otherwise you might run into situations where your view needs to access a control when it doesn’t yet have access to them. Just to be on the safe side, you should probably check for null before invoking any control method.

 

The last step is to let your view extends ViewWithUiHandlers or PopupViewWithUiHandlers instead of ViewImpl or PopupViewImpl. Then you’re ready to use your controls via getUiHandlers(). As a result, using the wonderful @UiHandler annotation is now very easy:

[sourcecode language=”java”]
public class ExampleView extends ViewWithUiHandlers<ExampleView.MyUiHandlers> implements MyView {

@UiHandler("saveButton")
void onSaveButtonClicked(ClickEvent event) {
if (getUiHandlers() != null) {
getUiHandlers().onSave();
}
}
[/sourcecode]

That’s it, you now have a powerful, easy to read and versatile way of using your MVP elements without giving more responsibilities to your view and without breaking the MVP pattern. Your presenter still does all the heavy lifting and your view is still pretty dumb so testing should be a breeze!

 

  • Well, I think we rushed this one. “Reversing” in the title is mainly for people that wasn’t using that pattern before, for someone that was already doing this all allong, it’s confusing. This is pure MVP as explained by Martin Fowler and his Supervising Controller and isn’t really Reversing the MVP pattern.

    We still have some discussions about naming convention, but how to use it in your work won’t change. I’ll keep you informed.

  • Jon Brule

    Thanks for the great article to help clear up this enigma within gwt-platform! One question, however, where is ExampleView.MyControls defined? Is there a parallel interface to MyUiHandlers that the view must implement to specify the view’s controls? Or, is it something else entirely?

  • Omg, MyControls was a Typo! That should have been MyUiHandlers! My bad, we changed the interfaces in development and I updated this thread to reflect the changes but I forgot this small part 🙁

  • Jon Brule

    Thanks so much for clearing that up! My brain was in a tailspin since the coffee had not yet set in… Have a great day!

  • Thanks for writing this up! I needed to make the following change to my own code in the presenter to get things working properly:


    public interface MyView extends View, HasUiHandlers {
    }

    to:


    public interface MyView extends View, HasUiHandlers {
    }

    • Oops…. I forgot that angle brackets will be removed. The second one should have a templated “MyUiHandlers” argument for HasUiHandlers.

    • No, it’s supposed to be HasUiHandlers. In your presenter you want to do:

      getView().setUiHandlers(this); and HasUiHandlers is only there to tell your presenter that this methods is present inside your view.

      You presenter must Implements MyUiHandlers to be able to call those methods from your view.

    • JROSESOL

      Well if I don’t use the brackets

      public interface MyView extends View, HasUiHandlers {
      }

      I get this error in MainPageView (your ExampleView):

      “The interface HasUiHandlers cannot be implemented more than once with different arguments: HasUiHandlers and HasUiHandlers”

      • Aside the fact that you need the brackets when declaring an interface, that’s a weird error message.

      • JROSESOL

        I meant angle brackets (they are invisible when I use them):

        public interface MyView extends View, HasUiHandlers”MyUiHandlers” {}

        Testing the angle brackets… not used to this.

        public interface MyView extends View, HasUiHandlers {
        }

        • Omg, sorry for the misunderstanding !

          I will need your presenter declaration and your view declaration to help you with this. At first sight, everything seems to be correct.

  • Jorrit Posthuma

    About this part:

    We usually call that interface MyUiHandlers and place it inside the view to keep things organized:

    When I’ve got


    public class MainPagePresenter ... implements MainPageView.MyUiHandlers {
    public interface MyView extends View, HasUiHandlers { ... }
    }

    and


    public class MainPageView ... implements MainPagePresenter.MyView {
    public interface MyUiHandlers extends UiHandlers { ... }
    }

    It creates a loop between the 2 classes

    • There’s a hight probability that changing:
      public class MainPagePresenter … implements MainPageView.MyUiHandlers
      for
      public class MainPagePresenter … implements MyUiHandlers

      Will remove that dependency or simply get the interface outside of the class in is own class and you will never have that problem again 😀 I personnally do it that way and don’t get this error.

      Cheers,

      • Jorrit Posthuma

        That was indeed my temporal solution (moving it outside). However, I already thing that GWT(P) use a lot of classes. I like the solution of “combining” those using inner classes/interfaces.

        I’ll let it rest for now 🙂

      • I think the best way to keep modularity is to get the interface outside of the class. The view should be as easy as possible to replace. If you use inner classes in the views and want to replace them at some point, you must also change the presenter – your buisness logic. I think that should be avoided.

        • I agree, but truth is, with a presenter, the view is a Singleton with a 1-to-1 relation to the presenter. In that case, it’s a personnal choice to create it inside or outside the view, because even for modularity, there’s no real gain. I prefer to put it inside because in larges projects, I try to avoid as much as possible to create too many class.

      • Myself and others have encountered an intermittent problem with javac complaining about this (seems to depend on the import order!) in fact, it looks like it’s a javac bug and that it should be complaining all the time about the loop. As a result, we now recommend moving MyUiHandlers out of the view and calling it ExampleUiBinder.

        More details in the official GWTP doc.

  • Jason A Hatton

    We actually have independently determined this is an valuable approach. There were discussion on purity because there are different interpretations of MVP and what it means. But, I chose less classes over purity any day if there is no long term value in keeping things “pure”.

    We have noticed a benefit of easier and cleaner tests on our Presenters as well.

  • polyk

    This was a good help, but testing becomes very difficult because you can’t test the view without the presenter.
    How can we have good unit tests ?

    • Hi Polyk,

      When I use unit test, I usually mock all collaborators and in this case the presenter is a good candidate to be mocked. Using Mockito it becomes really easy to isolate the behavior of the view.

      For more complete test, I use Cucumber.

      Cheers,

      • polyk

        thanks for the answer , but I’m wondering how you can mock ui fields when using uiBinder. Because you can’t instantiate them since they are in a file (myFile.ui.xml).

  • Hi Polyk, that’s where this other article come in handy:
    http://arcbees.wordpress.com/2010/11/25/testing-views-using-gwt-platform-mockingbinder/

    Cheers 😀