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:

public interface MyUiHandlers extends UiHandlers {
  void onSave();
}

Your presenter then needs to implement this interface:

public class ExamplePresenter extends Presenter<ExamplePresenter.MyView, ExamplePresenter.MyProxy>
    implements MyUiHandlers {
  ...
  @Override
  public void onSave() {
    doSomething();
  }
  ...
}

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:

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);
  }
  ...
}

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:

public class ExampleView extends ViewWithUiHandlers<ExampleView.MyUiHandlers> implements MyView {
  ...
  @UiHandler("saveButton")
  void onSaveButtonClicked(ClickEvent event) {
    if (getUiHandlers() != null) {
      getUiHandlers().onSave();
    }
  }

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!

 

22 comments

  1. Christian Goudreau · September 5, 2010

    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.

  2. Jon Brule · September 15, 2010

    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?

  3. Christian Goudreau · September 15, 2010

    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 ๐Ÿ˜ฆ

  4. Jon Brule · September 15, 2010

    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!

  5. Neil Halelamien · September 22, 2010

    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 {
    }

    • Neil Halelamien · September 22, 2010

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

    • Christian Goudreau · September 22, 2010

      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 · December 15, 2010

      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”

      • Christian Goudreau · December 15, 2010

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

      • JROSESOL · December 15, 2010

        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 {
        }

      • Christian Goudreau · December 16, 2010

        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.

  6. Jorrit Posthuma · September 22, 2010

    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

    • Christian Goudreau · September 22, 2010

      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 · September 22, 2010

        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 ๐Ÿ™‚

      • Flori · September 28, 2010

        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.

      • Christian Goudreau · September 28, 2010

        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.

      • Philippe Beaudoin · December 9, 2010

        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.

  7. Jason A Hatton · October 1, 2010

    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.

  8. polyk · May 3, 2013

    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 ?

    • Christian Goudreau · May 3, 2013

      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 · May 6, 2013

        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).

  9. Christian Goudreau · May 6, 2013

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

    Cheers ๐Ÿ˜€

Leave a comment