Let’s start with the example I’ve already used, a login form like this:
Your view has two text fields (username, password) and a button (login), as well as a few labels, possibly a title and some explanatory text. We’ll assume that the presenter needs to interact with the text fields and buttons, but that the labels, title and any explanatory text can be encapsulated into the view.
When you build your presenter, you’ll need to decide how the view and the presenter will interact, and there are a few approaches you can take.
Abstracted Control ViewRay Ryan’s Best Practices for Architecting your GWT App uses views that act as containers for controls. Rather than returning the widgets themselves (e.g.
TextField), the views return interfaces that abstract the specifics of the control from the presenter. This allows the presenter to be completely decoupled from GWT widgets classes, as well as increasing your ability to substitute one control for another.
So, for our example login view, we’ll need to expose the username, password and login button, and we can do so through interfaces already supported by these widgets. For instance, the
PasswordTextBoxwidgets used for the username and password respectively implement the
HasTextinterface, which would allow you to retrieve the text that has been entered, and the login button supports
HasClickHandlers, which would allow you to add a
ClickHandlerevent-handling class to the login button.
For our simple case, an abstracted control view would look like this:
This works well for simple cases, but quickly gets complicated. For instance, what if you want to expose an aspect of a control that doesn’t have a convenient interface, like
setVisible(), which most GWT widgets inherit from
UIObject, but which isn’t part of an interface.
One alternative would have you create the interfaces that you need, extend GWT widgets and implement these interfaces. For instance, you could create
HasVisibility, then create a new class,
UIObject, each class that inherits from
UIObjecthas the same consistent method signature, so as long as you’re willing to apply this subclassing approach pervasively, you can create the necessary interfaces.
This also gets complicated when you need to expose two or more aspects of one widget. For instance, if your login view chose to change the text on the login button to “logging in …” and disable it while the login process was underway, this requires you to expose three aspects of the login button — enable/disable, setting the text, and adding a click handler.
You can do this by grouping the interfaces you need to expose into interfaces that expose the exact right combination for a given view. You could build interfaces like like
While this gives you lots of control over the level of abstraction, and gives you a lot of substitutability (
StyledTextcould be implemented by
VisibleClickablecould be implemented by
Anchor), this approach can quickly get out of control. On a project of any size, there are likely to be more combinations here than you care to count let alone implement.
Ultimately, if you end up exposing multiple aspects regularly, you’re probably better off accepting that it’s too awkward to maintain every combination of exposed aspects as a separate interface, and simply create an interface that combines all of the aspects you want to expose, either for each class, or for a cluster of related clsses. For instance, you might create a ‘TextInput’ interface that combines all the interfaces that you wish to expose on a
TextField, and then create a
TextInputBoxclass that extends
As an alternative, you could expose each aspect of a control through an independent accessor. Using the previous example, you could expose the login button control through independent methods that expose the enable/disable, text setting and click handling:
This style is fairly similar to the Interaction View style which follows; most people will find the Interaction View style more appealing.
Interaction ViewWhere the Abstracted Control View exposes abstractions of controls, the Interaction View exposes the interactions themselves.
For instance, although our login view has three controls, it has six known interactions:
- get the username text
- get the password text
- add a click handler for the login button
- set the login button text
- disable the login button
- enable the login button
Each of these can be exposed through a single method:
Alternately, you might choose to group the interactions if they’re likely to be invoked together:
This is closer to the approach used by Daniel Danilatos in GWT Testing Best Practices. If most of your controls expose a large number of unique aspects, this can get tedious — where an abstracted control view might need a single method per control, the interaction view might require several times that. Imagine a data-entry screen with twenty controls and complex behavior to show/hide fields, change styles, display field-level validation, and you'll start to see that this could get pretty messy. In practice, I find that relatively rare, but your mileage may vary depending on your project.
In addition, Interaction View tends to mesh well with mock testing, which is sometimes called Interaction-based testing, in that it allows the test to more clearly expect the intent, rather than needing to mock controls and expect certain messages to be passed to the controls.
View DelegateFinally, there’s the view delegate style, where the view handles the events, but delegates them to the presenter. This is of particular merit if you’re using GWT UiBinder and its support for
@UiHandlerto reduce boilerplate code, and can be seen in Large-Scale Application Development and MVP. In this model, the view will respond to the events of its controls, but delegate the processing of that event to the Presenter.
In the case of our
LoginView, this is something you’d use for the login button click event:
This event delegate style fits most naturally as an extension of the Interaction View.