Lately I've been doing a lot of research and development around patterns and unit testing. One pattern in particular, Model View Presenter (MVP), I found to be a very good pattern for doing web development. The only thing, is that if you want to learn about the MVP, there seem to be two options. 1) Completely theoretical in which you talk about the design and what its supposed to accomplish or 2) Simple - very simple examples on how to use the MVP - that doesn't show how to expand the pattern. So, I thought I would give it a shot to write about a more complex MVP pattern to try to handle real world scenarios.
Here's some requirements for this example:
Lets get started:
I start off by creating a new Control Library project that contains 3 controls:
I'll start with the Animal Control. The first thing I want to do is define my interface, I do so by creating a structure that looks like this:
namespace MVP_Example
{
public interface IAnimalView
string MyNoise { get; set; }
string MyHappyGesture { get; set; }
}
I then create my Animal Control using the new interface and inheriting from WebControl. This is the main control that handles most of the details I would like to display, and the main implementation of the view. I added comments into the code to explain what I'm doing:
public class AnimalControl : WebControl, IAnimalView
private AnimalPresenter _presenter;
private string _myNoise;
private string _myHappyGesture;
protected override void OnInit(EventArgs e)
base.OnInit(e);
//pass in this control (the view) to the presenter
_presenter = new AnimalPresenter(this);
protected override void Render(HtmlTextWriter writer)
//Here I custom render myself
writer.RenderBeginTag(HtmlTextWriterTag.P);
//output my noise property as defined in the view
writer.Write(string.Format("My Noise is {0}", MyNoise));
writer.RenderEndTag();
//output my gesture property as defined in the view
writer.Write(string.Format("I'm happy by {0}", MyHappyGesture));
#region IAnimalView Members
public string MyNoise
get
return _myNoise;
set
_myNoise = value;
public string MyHappyGesture
return _myHappyGesture;
_myHappyGesture = value;
#endregion
For my Animal Control I also have the presenter which sets up the default values:
public class AnimalPresenter
private IAnimalView _view;
public AnimalPresenter(IAnimalView view)
_view = view;
_view.MyNoise = "Making Noise";
_view.MyHappyGesture = "Wagging My Tail";
Next, I want to use that Animal Control, but for my dog control it barks as a noise, so I want to override that property. I inherit from the Animal Control to get the implementation of IAnimalView that I just coded. The only difference is that I have a special DogPresenter that inherits from Animal Presenter so that I can use that functionality that I already coded. You'll notice how empty the control is, and that is good because you have all your logic where it should be!
Here is the Dog Control:
public class DogControl : AnimalControl
private DogPresenter _presenter;
_presenter = new DogPresenter(this);
Here is my Dog Presenter:
public class DogPresenter : AnimalPresenter
public DogPresenter(IAnimalView view)
: base(view)
_view = view; // get the view from the control
// add my own special noise, but keep the generic animal gesture
_view.MyNoise = "Bark";
I do the same with the Cat Control:
public class CatControl : AnimalControl
private CatPresenter _presenter;
_presenter = new CatPresenter(this);
Now with the cat presenter, it has its own noise and gesture, so I override those:
public class CatPresenter : AnimalPresenter
public CatPresenter(IAnimalView view)
_view.MyNoise = "Meow"; // add my own special noise
_view.MyHappyGesture = "Purring"; //I have my own happy gesture
Now to display these I just add a reference to the control library and add the controls to the page (here are just the lines I added to the aspx):
<%@ Register Assembly="MVP_Example" Namespace="MVP_Example" TagPrefix="animal" %>
And the output is as follows:
animal: My Noise is Making Noise I'm happy by Wagging My Tail dog: My Noise is Bark I'm happy by Wagging My Tail cat: My Noise is Meow I'm happy by Purring
My Noise is Making Noise
I'm happy by Wagging My Tail
My Noise is Bark
My Noise is Meow
I'm happy by Purring
There are a few different ways to accomplish some major tasks and having your base functionality isolated and then use that base functionality in other classes. With the MVP pattern you can pull out the logic from the user interface and keep it in the presenters. The example from above with the inheritance is extremely useful if you have a lot of data to get off the http request everytime, such as the url query string items, etc. Having unique implementations of the presenter for your controls is useful for having your own databinding implementations, etc.
Download the code: http://brob.members.winisp.net/MVP_Example.zip (Example was created with VS 2008 Beta 2)