CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Jeffrey Palermo (.com)

Blog moved to www.jeffreypalermo.com

Trying out the Model-View-Presenter pattern - level 300

After reading Martin Fowler's Model View Presenter article, I did some deep thinking about how to implement that pattern.  The purpose of the pattern is to decouple UI process logic from the UI container.  If  you think about the code-behind classes in your UI, all the examples we see have button-click handlers performing process logic.  Below is my first attempt at creating a behavior layer for UI testability and portability:

I'll present this example using a simple ASP.NET page:

<%@ Page language="c#" Codebehind="default.aspx.cs" AutoEventWireup="false" Inherits="ModelViewPresenter.WebForm1" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
 <body>
  <form id="Form1" method="post" runat="server">
   <asp:textbox id="txtName" runat="server">
   </asp:textbox><asp:button id="btnAdd" runat="server" Text="Add Name"></asp:button>&nbsp;
   Number of items:
   <asp:label id="lblCount" Runat="server"></asp:label><br>
   <asp:datagrid id="dgdNames" runat="server">
    <Columns>
     <asp:ButtonColumn CommandName="delete" ButtonType="PushButton" Text="Remove" />
    </Columns>
   </asp:datagrid></form>
 </body>
</HTML>

 

This page is used to maintain a list of names.  Type in a name and click the add button; remove a name from the list.  The current names are shown in a datagrid, and the count is shown next to the button.  Here is the code-behind:

   14 public class WebForm1 : System.Web.UI.Page, Presenter.INamesView
   15 {
   16     protected System.Web.UI.WebControls.TextBox txtName;
   17     protected System.Web.UI.WebControls.Button btnAdd;
   18     protected System.Web.UI.WebControls.DataGrid dgdNames;
   19     protected System.Web.UI.WebControls.Label lblCount;
   20 
   21     private Presenter.NamesPresenter _presenter;
   22 
   23     private Model.Names _names {
   24         get { return (Model.Names)ViewState["names"]; }
   25         set { ViewState["names"] = value; }
   26     }
   27 
   28     private void Page_Load(object sender, System.EventArgs e)
   29     {
   30         _presenter = new Presenter.NamesPresenter(this);
   31         if(!IsPostBack) {
   32             _presenter.Load();
   33         }
   34     }
   35 
   36     public Model.Names Names {
   37         get { return _names; }
   38         set { _names = value; }
   39     }
   40 
   41     public void Update() {
   42         dgdNames.DataSource = _names;
   43         dgdNames.DataBind();
   44 
   45         lblCount.Text = _names.Count.ToString();
   46     }
   47 
   48     private void btnAdd_Click(object sender, System.EventArgs e) {
   49         _presenter.AddName(txtName.Text);
   50         txtName.Text = string.Empty;
   51     }
   52 
   53     private void dgdNames_DeleteCommand(object source, DataGridCommandEventArgs e) {
   54         _presenter.RemoveName(e.Item.ItemIndex);
   55     }

 

I have omitted the using statements and the IDE-generated code, so this is not complete, but it shows how I am delegating behavior to the presenter layer.  Here is my presenter:

    5 public class NamesPresenter
    6 {
    7     private INamesView _view;
    8 
    9     public NamesPresenter(INamesView view) {
   10         _view = view;
   11     }
   12 
   13     public void AddName(string nameToAdd) {
   14         _view.Names.Add(nameToAdd);
   15         _view.Update();
   16     }
   17 
   18     public void RemoveName(int indexOfNameToRemove) {
   19         _view.Names.RemoveAt(indexOfNameToRemove);
   20         _view.Update();
   21     }
   22 
   23     public void Load() {
   24         _view.Names = new Model.Names();
   25         _view.Update();
   26     }
   27 }

 

This presenter controls the model (my business object) and tells the UI what to do and when through the interface that the UI must implement (below):

    5 public interface INamesView
    6 {
    7     Model.Names Names{get;set;}
    8     void Update();
    9 }

 

As long as my UI implements this interface, my presenter can control it, and I can make changes to the UI (including total replacement) without impacting the core behavior. 

Next, all this exists to manage my model or business object:

    6 [Serializable]
    7 public class Names : IEnumerable
    8 {
    9     private ArrayList _entries = new ArrayList();
   10 
   11     public string this[int index] {
   12         get { return _entries[index].ToString(); }
   13         set { _entries[index] = value; }
   14     }
   15 
   16     public int Count {
   17         get { return _entries.Count; }
   18     }
   19 
   20     public void Add(string name) {
   21         _entries.Add(name);
   22     }
   23 
   24     public void RemoveAt(int index) {
   25         _entries.RemoveAt(index);
   26     }
   27 
   28     public IEnumerator GetEnumerator() {
   29         return _entries.GetEnumerator();
   30     }
   31 }

 

Events from the UI are delegated to the presenter, which decides what to do and calls interface methods on the UI.  The presenter controls interaction between the UI and the model just as a repository layer would control presistence between the model and a data store.

 

I don't pretend that this example is perfect, and I'm open for criticism, but the model view presenter pattern for the UI is very tempting for testability and UI portability.



Comments

Dim Blog As New ThoughtStream(me) said:

I spent some time this weekend, doing some R&amp;D on the Model View Presenter pattern, to get myself...

# January 22, 2007 9:46 AM

About Jeffrey Palermo

Jeffrey Palermo is a software management consultant and the CTO of Headspring Systems in Austin, TX. Jeffrey specializes in Agile coaching and helps companies double the productivity of software teams. Jeffrey is an MCSD.Net , Microsoft MVP, Certified Scrummaster, Austin .Net User Group leader, AgileAustin board member, INETA speaker, INETA Membership Mentor, Christian, husband, father, motorcyclist, Eagle Scout, U.S. Army Veteran, and Texas A&M University graduate. Check out Devlicio.us!

Our Sponsors

Proudly Partnered With


This Blog

Syndication

News

Headspring Systems

View Jeffrey Palermo's profile on LinkedIn

See my new blog at .jeffreypalermo.com