Simple MVP with generics

After a harrowing experience with a complicated web framework (WCSF**) I have recently been through the process of helping people to understand a simple form of the Model View Presenter pattern in ASP.NET. At it’s simplest MVP means pushing UI policy code for each page into a regular (and hence easier to test) C# object called the Presenter. Once you have this object, the view (page) can tell it when the user does something (or some other event occurs like the page is loaded) and the presenter can tell the view what to do. Here it is in code form:

public partial class SimplePage : System.Web.UI.Page
{
protected readonly SimplePresenter Presenter;

public SimplePage()
{
this.Presenter = new SimplePresenter(this);
}

protected void Page_Load(object sender, EventArgs e)
{
Presenter.ViewReady();
}

public void PresentMessage(string message)
{
MessageLabel.Text = message;
}
}

public class SimplePresenter
{
protected readonly SimplePage View;

public SimplePresenter(SimplePage View)
{
this.View = View;
}

public void ViewReady()
{
View.PresentMessage("Hello, World!");
}
}



Easy enough. In this case the presenter is tightly coupled to the view (which is bad) but we can easily get around this by extracting an interface from the page class and changing the field in the presenter to use the interface instead of the concrete implementation.



There is still a lot of plumbing code here that we’d need to duplicate for each and every View/Presenter pair. If you are willing to take up an Inversion of Control Container you could probably push a lot of this into container configuration but in many places it’s hard enough to get MVP under the radar, let alone IoC. Baby steps. We can hide some of the plumbing with some crazy generics-fu. Here it is:



public abstract class PresentedPage<T> : System.Web.UI.Page
where T : IPresenter, new()
{
protected readonly T Presenter;

public PresentedPage()
{
Presenter = new T();
(Presenter as IPresenter).BindToView(this);
}
}

public interface IPresenter
{
void BindToView(object view);
}

public abstract class Presenter<T> : IPresenter where T : class
{
protected T View { get; private set; }

void IPresenter.BindToView(object view)
{
View = view as T;
}
}


The idea here is that you would:




  • create a view interface of some kind




    public interface IHelloWorldView { … }



  • create a class which inherits from the presenter base class (substituting T for the view interface).




    public class HelloWorldPresenter : Presenter<IHelloWorldView> { … } 


  • create a new page class and change the base class in the code behind to inherit from the view interface and PresentedPage<T> where you’d substitute T for your presenter class.




    public class HelloWorld : IHelloWorldView, PresentedPage<HelloWorldPresenter> { … }




That’s a few steps but simple enough really. Let’s see the end result for our Hello World app:



public interface IPageWithPresenterView
{
void SetMessage(string message);
}

public class MyPresenter : Presenter<IPageWithPresenterView>
{
public void ViewLoaded()
{
View.SetMessage("Hello World");
}
}

public partial class PageWithPresenter : PresentedPage<MyPresenter>, IPageWithPresenterView
{
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
Presenter.ViewLoaded();
}

public void SetMessage(string message)
{
lblMessage.Text = message;
}
}



In reality I’d probably introduce some of the more common things into the base classes like ViewReady (called for each request) and ViewInitialized (called the first time. i.e. Not on PostBacks) so that you don’t need to recreate them each time.



Why do you want to do this anyway? Well, the presenter is just a class and it has no idea what it’s talking to as it communicates via an interface. It could be controlling:




  • An ASP.NET WebForm


  • A Silverlight Control


  • a Windows Forms control


  • A Text-To-Speech system


  • A Braille Screen


  • A car dashboard control


  • A batch system that reads data from a text file and outputs to a printer in Nebraska (why not?)


  • … pretty much anything!



And yet we can tell just by looking at the presenter that when the View is loaded, the user will receive the message “Hello World”. It looks like serious overkill for something like Hello World but when you end up with more complicated logic it can work quite well.



The term View is misleading because it implies a screen. The View in this instance is really just a Human Computer Interface like a mouse or a keyboard but with business-specific functions instead of general purposes ones. It can be used to tell the user things, and ask the user things but the logic, the business decisions about display, are stored in the presenter.



There most obvious advantage of doing this is in testing. If the presenter is making all the decisions then it is the component that requires the most testing. As the presenter has no idea what it’s really bound to you can easily pass in a fake view for testing purposes so the Presenter is also the easiest component to test.



MVP really doesn’t have to be hard and it doesn’t require a huge complicated framework. Give it a go. You might like it.



** I’m not really having a dig at WCSF here. It just has 500 functions and we needed 2.

mvpasp-netgenerics
Posted by: Mike Minutillo
Last revised: 27 May, 2011 02:42 PM History

Comments

No comments yet. Be the first!

No new comments are allowed on this post.