Software Development Magazine - Project Management, Programming, Software Testing |
Scrum Expert - Articles, tools, videos, news and other resources on Agile, Scrum and Kanban |
Click here to view the complete list of archived articles
This article was originally published in the Spring 2012 issue of Methods & Tools
MVP Foundations for Your GWT Application
Adam Tacy, Robert Hanson, Jason Essington Ian Bambury, and Christopher Ramsdale, http://www.manning.com/tacy/
This article is based on GWT in Action, Second Edition, to be published in August 2012. This eBook is available through the Manning Early Access Program (MEAP). Download the eBook instantly from manning.com. All print book purchases include free digital formats (PDF, ePub and Kindle). Visit the book’s page for more information based on GWT in Action, Second Edition. This content is being reproduced here by permission from Manning Publications.
One of the main selling points of GWT is that it allows you to use an industry-grade language, with an industry-grade set of tools, to build...well...industry-grade web apps. But, as with any large scale development project, you can easily paint yourself into a corner. Far too many times when building GWT-based apps, we find ourselves slinging code wherever necessary to make the app work, and (sometimes more importantly) look good.
Fortunately, there is a well known solution to this problem: build your applications based on the model-view-presenter (MVP) paradigm.
Architecting your GWT-based apps to utilize the Model View Presenter (MVP) paradigm provides the foundation and rails necessary to avoid some common pitfalls.
Let’s see first how the user sees the application and then what we will be doing from the MVP perspective.
From the user’s perspective
Figure 1 shows the screens that can be found in our sample photo application.
Figure 1 The three views shown by the photo application. The user starts at the WelcomeView, clicks a button to see the PhotoListView, then clicking a photo brings up the PhotoDetailsView.
When you start the application, you are taken to a welcome screen, where you can request a list of photos by clicking the View Photos button or going through the menu bar options. While the list of photos is being retrieved, a busy message is displayed to the user.
Our photographer seems to have been in one of their abstract moods for this photo set because these are all just blocks of color. You can click on one of the photos, and a larger version is shown on its own, and here you can edit the title through an editable label where the new title is saved back to the server, if requested.
It really is a nice way to see the underlying implementation using MVP.
From the MVP perspective
At its core, MVP is a design pattern that breaks your app up into the components listed in table 1, the right-hand column of which explains how this is done for PhotoApp.
Table 1 Components of the MVP paradigm
MVP Component |
Description |
Model |
Houses all of the data objects that are presented and acted upon within your UI. The number and granularity of models is a design decision. In our PhotoApp, we will have one very simple model: PhotoDetails - holds data about a photo, including the thumbnail URL, original URL, title, description, tags, and so on. (Our model will have data filled from a server component. We implement that as a very simple mock-up component that reads data from a file and. when "writing" just implements a delay before returning; you could extend that to read from a database, or make a call to a web service, and so on.) |
View |
These are the UI components that display model data and send user commands back to the presenter component. It is not a requirement to have a view per model—you may have a view that uses several models, or several views for one model. We will keep things simple for our Photo Application, and will have three views: WelcomeView - a very simple welcome page. PhotoListView - displays a list of thumbnail photos and their title PhotoDetailsView - displays the photo together with title and other data and allows the user to change some of those details. |
Presenter |
The presenter will hold the complex application and business logic used to drive UIs and changes to the model. It also has the code to handle changes in the UI that the view has sent back. Usually for each view, there will be an associated presenter. In our photo application, this means we will have the following three presenters: WelcomePresenter - pushes the welcome screen in front of the user, and handles the jump to PhotoListView. PhotoListPresenter - drives the thumbnail view. PhotoDetailsPresenter - drives the view of the original photo. |
The concept is that a presenter creates its associated view and requests the view to be populated with appropriate data from the model. When UI elements in the view are updated, they notify the presenter to change the model. This way, the view doesn’t need to know about the model, making it easy to swap and maintain.
A quick note on connecting views and presenters
The more contemporary MVP setup you may have read about in previous articles, blog posts, and so on, would indicate that presenters register to receive events raised from the view’s widgets.
However, you will see that we specifically make the presenter register itself in the view, and that the view notifies the presenter by calling appropriate registered presenter methods when events are raised.
Navigation to a new view is performed either by the application updating the URL in the browser as a result of the user doing something, or the user clicking the forward/back buttons. The application will react to changes in the browser’s URL and creating the appropriate presenter (which then creates the view). We will call this the Application Controller or AppController for short.
We will also make use of an EventBus. We’ll use this to raise application-wide events. To keep to the MVP paradigm, we need to be careful that these events do not cause changes in the model. What is their use then? Well, in our application we will allow various presenters to raise ApplicationBusy and ApplicationFree events, for example, when starting and completing communication with the server. These events will be dropped onto the EventBus by the presenters, and the AppController will listen for them and react by showing and hiding a busy message.
Using a ClientFactory
Throughout the two photo app projects, you will see us use a ClientFactory object. It will provide access to singleton instances to common objects across the application. For example, it provides access to our views as singleton objects, and this improves efficiency because views contain DOM calls, which are expensive.
There’s no requirement to use a ClientFactory in the MVP paradigm, but it is helpful if we are thinking of enterprise-wide development.
Between a presenter and its view, there is a strong connection, but loose coupling. Nice words, but what does it mean? Well, let’s crack open some of the photo apps code and have a look.
Creating Views
Remember that our view should have no application logic in it, at all. It should be just UI components that the presenter can access to set or get values from.
All of our views will be implemented as three separate items: a generic interface, a specific interface and an implementation. The next three sections will look at each of those in turn, starting with the generic interface.
The root of all views
Each of our views will be built upon a basic interface that we will call View. This is not a requirement to have, but suits us well as it creates a basic contract that all our views will adhere to, and that other components are aware of and can trust. Here it is:
public interface View extends IsWidget{ void setPresenter(PhotoDetailsPresenter presenter); }
There are two key things to note about this interface. Any implementation must implement the setPresenter method - which allows the presenter to register itself with this view (this is the topic of section 14.1.3 so we will leave that for now). The other is that all our views will extends the IsWidget interface. This means each view will implement the asWidget method, which we will have to implement so that it returns a representation of the view that is a widget. Luckily, since most views will be made up from a Composite which, from GWT 2.1 onwards, already implements asWidget, we are good to go with no extra work!
Each specific view needs to be described individually, and for that, we create first a new interface.
View specific interface.
For each view we will have, we create a new interface that extends View. These view specific interfaces define the methods that a presenter can call to get access to the UI components. As an example, our PhotoDetailsView will have a title that can be changed and so the value of needs to be retrieved. We can make the interface show that by saying it must implement a getPhotoTitle method that returns an object implementing the HasValue<String> interface:
public interface PhotoDetailsView extends View { HasValue<String> getPhotoTitle(); }
Our application has three views, so you can find the three interfaces in the source code, for example in the com.manning.gwtia.ch14.client.mvp.views package.
With the details of the views now specified in the interfaces, we need to implement them.
Implementing the views
Our view specific interfaces detail exactly what can be expected from the view. But without an implementation, they are not going to do much!
It turns out that our implementation of the PhotoDetailsView interface is pretty simple. That should be as expected as the view is dumb and contains no logic. The first two chapter examples are the same, except they differ in how views are implemented. In the first example, the implementations are verbose as we have a lot of boilerplate code; in the second, we have swapped the view implementation for UiBinder versions.
The only difference between the two versions is the use of UiBinder, and therefore how we bind events to actions. The actual actions do not change. Take the detailed view, each implementation implements the setPresenter method to register the parameter as this object’s presenter:
public void setPresenter(PhotoDetailsPresenter presenter) { this.presenter = presenter; }
Each view also implements a reaction to the title being changed. In the UiBinder approach it is neatly described as follows
@UiHandler("title") public void changeTitle(ValueChangeEvent<String> event) { if (presenter != null){ presenter.onUpdateTitle(); } }
In other words, if the title widget raises a ValueChangeEvent, then as long as the presenter is not null, call the presenter’s onUpdateTitle method.
In the non UIBinder project, the same logic is there, but it is implemented by manually creating the title object and then adding a ValueChangeEvent handler to it. By convention, we tend to put all the binding code in a bind() method, if we are not using UIBinder.
So, our view is dumb and contains zero application logic. That is because we agreed that all of that should appear only in the presenter. And this brings us to the discussion of those presenters.
Presenters
Presenters are where all the application logic sits and will have no UI components. (Those are all in the view, as we’ve just seen.) In a similar way to views, we provide a generic presenter interface, a specific one, and an implementation for each presenter.
The root of all presenters
Just like views, we will create a basic interface that all of our presenters will implement. Again, there is no requirement to do this; it is just good to have. Here’s our basic interface:
public interface Presenter{ public void go(final HasWidgets container); public void bind(); }
The go method takes the widget in which we wish the presenter-associated view associated to be displayed. All we require of that widget is that it implements the HasWidgets interface; in other words, it is a panel of some kind.
A presenter will also implement a bind method. In our design, this is where the presenter will listen to any application-wide events it is interested in (for example, this is where it hooks into the event bus) as well as where it calls the associated views setPresenter method.
Each view will have an associated specific interface.
Presenter-specific interface
The specific functionality of the view is given in a new interface that implements Presenter.
In the specific presenter interface we wish to declare those methods that will be called from the view when things happen. Remember that we said the view is responsible for reacting to UI events within itself, but that it will then call methods on the presenter that was registered with it for the actual business logic. Here, we see the PhotoDetailsPresenter interface relating to the title:
public interface PhotoDetailsPresenter implements Presenter{ public void onUpdateTitle(); }
What we are saying here is that we expect the onUpdateTitle method to be called when the title is updated in the view. We would add other methods to the interface based on the other UI components that are of interest.
Now we have the specific presenter interface in place, we should implement it.
Implementing specific presenters
Our presenters are just implementations of the specific presenter interfaces. For example, PhotoDetailsPresenterImpl implements the PhotoDetailsPresenter interface.
This means it needs to implement the go and bind method from Presenter as well as the onUpdateTitle from PhotoDetailsPresenter. Listing 1 shows the skeleton of our implementation.
Listing 1 PhotoDetailsPresenter
public class PhotoDetailsPresenterImpl implements PhotoDetailsPresenter { private ClientFactory clientFactory = GWT.create(ClientFactory.class); public PhotoDetailsPresenterImpl(final PhotoDetailsView photoDetailsView , final String id) { this.rpcService = clientFactory.getPhotoServices(); |#1 this.eventBus = clientFactory.getEventBus(); |#1 this.photoDetailsView = photoDetailsView; |#1 eventBus.fireEvent(new AppBusyEvent()); #2 rpcService.getPhotoDetails(……) #3 bind(); } public void bind() { photoDetailsView.setPresenter(this); #4 } public void go(final HasWidgets container) { |#5 container.clear(); |#5 container.add(photoDetailsView.asWidget()); |#5 } |#5 public void onUpdateTitle() { |#6 rpcService.savePhotoDetails(……); |#6 } |#6 }
#1 Getting shared resources
#2 Firing application wide event
#3 Making a server call
#4 Binding to other items
#5 Implementing the go method
#6 Called from View when title updated
The first thing that is done in the constructor is to get hold of some common resources from the ClientFactory that we previously mentioned. This is not a requirement but makes our lives easier. For example, we grab resources such as the RPC service and the event bus - we might as well share the RPC service for efficiency, and we have to share the event bus; otherwise, it would not be system wide.
Once we have grabbed our resources from the factory, we make a call to get the details of the photo just after we have fired off a system-wide ApplicationBusy event. The intention here is that some other component will inform the user that the application is busy in some way—we don’t care at this point how that is done. Not shown in listing 1 is the fact that an ApplicationFree event is fired within the RPC return handling code so that the user is notified the application is no longer busy.
Within the constructor, we also call the bind method from the Presenter interface. For this implementation, that simply calls the view’s setPresenter method to register this view with that presenter. Other implementations may register on the event bus if it has to handle application wide events.
In #5, we are implementing the Presenter’s go method. This clears the container widget passed in as the parameter and then adds the associated view as a widget. In the application controller, we will create presenters in the following manner:
new PhotoDetailsPresenterImpl(clientFactory.getDetailsView()) .go(container)
Where container is the widget that we wish the presenter to present in the view.
That’s it really for the views and presenters. Just before we jump to the AppController, which is responsible for tying everything together and starting presenters, we want to take a little deeper look at the relationship between presenter and views we have used.
The two-way presenter/view relationship
At the heart of the MVP pattern is the relationship between your presenters and views. Presenter classes should contain all of your complex application logic, and no widget code/references. Completely inverse to that, your views should contain nothing more than widgets, and should avoid any application logic wherever possible.
Why? For two reasons: 1) fast testing with increased code coverage and 2) maximum code reuse when porting to other Java based platforms, for instance Android (written carefully in the MVP paradigm, the application logic of a GWT application can be reused and all you need to do is just replace the view component).
If you look at testing, enforcing this split between presenters and views offers another way to save more time/money/development frustration. Whenever you find yourself needing to test functionality within your app that relies on widgets or some Javascript Native Interface (JSNI), you’re going to need a browser context. This means you’re going to need to run it within a GWTTestCase, which means - you guessed it - it’s going to take a long time to run that test. So how do we fix this? Simple. Let’s not test them at all - if you make the view as dumb as possible, by moving all of your app logic out into the presenter, you should be left with a view that contains nothing more than GWT widgets. Given that these are continuously tested by the GWT team, doing so in our own tests is simply redundant and not required. And, where you do need to test your views, those tests should be few and far between and, in many instances, simply integration testing being driven by Selenium (or some Selenium-like testing suite).
Now that we agree on moving the app logic out of the view, we have to move the view logic out of the presenter. More specifically, we have to make sure our presenters are solely relying on plain old Java code. This is to ensure that they can be tested using a vanilla JUnit test (which runs in ~1/1000th of the time of a GWTTestCase).
But, we are also making the relationship between view and presenters slightly differently to what you might expect. The more contemporary MVP setup you may have read about in previous articles, blog posts, and so on, would indicate that presenters register to receive events from the view’s widgets.
However, you can see in the code above, we specifically make the presenter register itself in the bind method with the view via the view’s setPresenter method, and that the view listens for events and then calls the appropriate method in the presenter.
Figure 2 The coupling between presenter and view in our photo application.
Technically there is no reason not to make the presenter listen for events from the view widgets. But allowing the view to call back into the presenter makes using the UIBinder approach easy, as you can see in figure 2.
Up until now we have views and presenters for our PhotoApp, but we have no way of knowing what presenter to request (so no idea what view to show), or react to the user changing views. For that, we need to control the application.
Controlling the application
We need a mechanism to change views in our application. The most common one is to indicate in the browser URL the new view required, usually via a token on the URL, and then to react to that.
In our presenters, if a view change is needed, we will change the history token to an appropriate value. This happens, for example, when selecting a photo in the Photo List view. The click on a photo is registered in PhotoListViewImpl, which then calls back to the presenter saying which photo is selected. In the PhotoListPresenterImpl code, we have:
public void onSelectPhotoClicked(String id) { History.newItem(Tokens.DETAIL + "&" + id); }
All we are doing here is changing the history token to have a value defined in the Tokens class to represent Photo Detail view appended by the photo id the user is interested in. If we’re interested in photo number 00004, then our development mode URL becomes as shown in figure 3.
Figure 3 Development mode URL after photo 00004 has been selected to be shown in the detailed view
If we’re using history to store tokens, then we must set up our GWT history management to handle history changes and fire up the appropriate presenter. We do that in the AppController class, as shown in listing 2.
Listing 2 AppController showing bind() to History and other events
public class PhotoAlbumApp implements ValueChangeHandler<String> { #1 private void bind() { : History.addValueChangeHandler(this); #1 } public void onValueChange(ValueChangeEvent<String> event) { |#2 String token = event.getValue(); |#2 if (token != null) { |#2 if (token.startsWith("details")) |#2 doPhotoDetailsDisplay(token); |#2 else if(token.startsWith("list")) |#2 : |#2 } |#2 } |#2 private void doPhotoDetailsDisplay(String token){} |#3 private void doPhotoListDisplay(String token){} |#3 private void doWelcomeDisplay(String token){} |#3 }
#1 Implementing the History Management
#1 Binding events to actions in the code
#2 Handling history changes
#3 Changing the view
The AppController binds itself to listen to listen to history events in #1 by implementing the ValueChange interface as well as adding this object as the ValueChangeHandler in the History.
We must implement the onValueChange method (#2), which is called when the history changes by GWT’s history subsystem. This method we set up to simply parse the history token to determine what view is requested, and then is required, and will call one of the do methods in #3, if the history token is recognized and an action can be determined.
These do methods are responsible for creating the view and the presenter, and then calling the presenter’s go method. For example, if we have changed to the situation in figure 2, then the onValueChange method determines that the value detail means it needs to call the doPhotoDetailsDisplay method. This action shows the user the new photo details view with the requested image, while the AppController sits there waiting for the next history change.
Summary
The MVP pattern is extremely useful when building large, web-based applications with GWT. Not only does it help make code more readable, and subsequently more maintainable, it also makes it much easier to implement new features, optimizations, and automated testing. Speaking from experience, we can’t stress the testing benefits enough. It’s a fundamental part of writing real-world applications, but often times it’s overlooked because it’s left until the end, and is too much of a pain to integrate. When your app is developed using the MVP pattern, test cases are a piece of cake, so much so that you’ll want to write them first. Remember, test driven development is your friend.
More Java Knowledge
Methods & Tools Testmatick.com Software Testing Magazine The Scrum Expert |