Wednesday, July 29, 2009

MVP with GWT

MVP stands for Model View Presenter. It is a front end architecture that separates your model (data object) and the view via presenter.

A detailed description can be found in this link (http://www.martinfowler.com/eaaDev/uiArchs.html) written by Martin Fowler. The post is focused on why it's appropriate to use MVP than MVC in GWT project, and how to implement it.

It is tough to decide whether to use MVP or MVC, because both designs solve the problem. One good example that separates the two is that with MVC, it's always the controller's responsibility to handle keyboard and mouse events, while with MVP the GUI component itself initially handle the user's input, and delegate the interpretation of that input to the presenter. (Consider Struts or Spring MVC vs. Flex/Flash ).



A good friend of mine said that MVP is particularly interesting in the context of GWT when doing TDD (Test Driven Development) or just thorough testing all round. The reason is that testing widgets and widget interactions is generally slow because you need to create a UI environment (hosted mode or web mode usually) to run your tests in. By moving your application logic into the Presenter, you end up with code that can be tested fairly fast (with mock widgets). Since the widgets are simple there's less to go wrong in this harder/slower to test area.



In my opinion, the richer your client framework is (e.g. Flex/Flash, Silverlight ) the more you should favor MVP.

Here are the steps you need to do in building MVP compliant project.


1) Define a Page object. The page object represents a web page. You can even make it more granular by defining a Form object.



public class Page extends Composite implements PageView {
private presenter;

public Page() {
presenter = new PagePresenter(this);
TextArea ta = new TextArea();
ta.setCharacterWidth(80);
ta.setVisibleLines(50);
add(ta);

Button b = new Button("Comment", new ClickHandler() {
public void onClick(ClickEvent event) {
presenter.submitComment(ta.getValue());
}
});
add(b);

}
//This method is defined in PageView, and its being called
//by the presenter.
public void updateView(String text) {
Label commentLabel = new Label(text);
add(commentLabel);
}

}


Your page contains the presenter object, and it implements PageView.

2) Define your PageView interface. The PageView acts as a conduit between Page and the Presenter.


public interface PageView {

public void updateView(String text);

}


3) Define your Presenter object. The presenter is the class that contains all the business logic. It has reference to the services.


public class PagePresenter {

private PageView pageView;


public PagePresenter(PageView pageView ) {
this.pageView = pageView;
}

//You can use HasClickHandlers, Hastext or HasHTML, but I prefer just passing the
// data, if you dont need any other functionalities.
public void submitComment(String comment) {
if(comment != null) {
service.submitComment(comment);
//Do service calls here. When done, call
//pageView.updateView()
}
}
}