Blog Stats
  • Posts - 18
  • Articles - 0
  • Comments - 18
  • Trackbacks - 79

 

Thursday, March 30, 2006

Journeys with Model-View-Presenter and ASP.NET

On a previous post, I mentioned my introduction to Model-View-Presenter (MVP).  I've now gotten a chance to try it out on a new project and have been very pleased with the results thus far.  MVP provides an approach for clean separation of concerns within the ASP.NET presentation layer without dismissing the page event model.  MVP sees the code-behind, along with the ASPX page, as strictly part of the view and provides a patter to treat it accordingly.  Although the pattern itself is easy to understand after looking at an example, I expect it'll be another couple of years before strong MVP “best practices” emerge.  (I was surprised to find that MVP was actually conceived over ten years ago by IBM's Taligent and put to the test on Dolphin Smalltalk.  Even though it's been around for a while, it didn't get any substantial attention unitl recently with Martin Fowler's inclusion of MVP in his upcoming book.)

I'd like to put forth some rules of thumb for using MVP with ASP.NET that I've learned from a number of other sources and recent personal experiences.  These are very open to criticism...in fact, I encourage it!  By no means do I deliever these as true best practices of MVP - I hope to get as much feedback as possible concerning your own experiences and the applicability of these tips.  (I have not provided much with respect to MVP code samples since your favorite search engine can find you plenty.  The links below contain samples as well.)

MVP Tips:

  • Calls from the presenter back to the view should resolve to simple accessors; the accessors should only accept/return model objects or primitives - business objects, data transfer objects, datasets, etc.  (http://codebetter.com/blogs/jeremy.miller/archive/2006/02/01/137457.aspx)
  • The presenter constructor should accept the view it presents along with any services it may depend on (i.e. dependency injection).  The following example serves to illustrates:

private readonly IEditCommentView view;
private readonly ICommentDao commentDao;

public EditCommentPresenter(IEditCommentView view, ICommentDao commentDao) {
  this.view = view;
  this.commentDao = commentDao;
}

  • The presenter should never require a reference to System.Web.
  • Don't expose too much of how the view is implemented via its interface.  For instance, examine the following code...

public bool IsClassical {
  get { … }
  set {
    chkClassical.Checked = value;
    txtComposer.Enabled = value;
  }
}

In the above example, you could instead provide two setters on the view:  IsClassical and ComposerEnabled.  The downside to having two setters is that you've bound a very minor implmenation detail to the interface - arguably, you should be able to easily switch concrete views and have presentation variations among them all.  The counter-point is that the view should remain completely ignorant of business decisions and should leave all details up to the presenter; therefore, the view should have two setters:  IsClassical and ComposerEnabled.  Additionally, giving ComposerEnabled its own setter allows it to be disabled for security reasons; regardless of the other settings.  (This example and discussion was brought up at http://codebetter.com/blogs/ben.reichelt/archive/2005/11/18/learning_model_view_presenter.aspx.)  Personally, I've found the single setter to be cleaner and promotes a more cohesive interface.

  • It's common practice to partition the GUI into separate user controls for proper separation of concerns and maintainability.  An effect of this is that it becomes easy to define the view/presenter relationship from the code-behind of the containing ASPX page.  The following example illustrates the parent page determining this relationship between a contained usercontrol (i.e. the view defined as commentListing), and the usercontrol's presenter (ListCommentsPresenter):

ListCommentsPresenter presenter = new ListCommentsPresenter(commentListing, DaoFactory.GetCommentDao());
commentListing.AttachPresenter(presenter);

if (!IsPostBack) {
  presenter.Init();
}

The call to AttachPresenter binds the presenter to a private member within the user control.  With this approach, the usercontrol never has to know how to create the presenter nor need knowledge of the constructor arguments that go along with the presenter's creation.  Finally, the usercontrol doesn't have to worry about when to call Init.  (This implementation tip comes from http://codebetter.com/blogs/jeremy.miller/archive/2006/02/01/137457.aspx.)

  • Views should provide a DisplayMessage property to the presenter so as to return feedback concerning the execution of an operation.  Alternatively, the presenter could return a “presenter result“ object to indicate what should happen next:  redirect, stay on the page, go to login, etc.  (This latter tip came from Mike Mason's blog that's accessible via Google cache.)
  • Unit tests should be used for guiding the communications protocol between a view and presenter.  Implementing the view itself should always be the last thing you do - NUnit and FitNesse can be used to validate most of the presentation before writing a scratch of HTML.  The primary benefit of this is ensuring you've done a good job of cleanly separating all possible business logic out of the view.

There are certainly other topics of discussion concerning MVP, but the above should be a good start with respect to “better“ MVP practices.  Please send feedback, comments and criticisms my way!

Billy

 

 

Copyright © Billy McCafferty