Delen via


Model View Presenter - with base functionality

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:

  1. Have a control library to handle generic/default implementations
  2. Have different implementations of the same control, but not have to re-write a ton of the logic.
  3. Server controls will write out their properties.
  4. Animals with different two (or N) types.  For this example I'll use Dog and Cat.

Lets get started:

I start off by creating a new Control Library project that contains 3 controls:

  1. Animal Control - this will contain the default behavior
  2. Dog Control - this will have dog specific behavior
  3. Cat Control - this will have cat specific behavior

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:

namespace MVP_Example

{

    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();

 

            writer.RenderBeginTag(HtmlTextWriterTag.P);

            //output my gesture property as defined in the view

            writer.Write(string.Format("I'm happy by {0}", MyHappyGesture));

            writer.RenderEndTag();

 

        }

 

        #region IAnimalView Members

 

        public string MyNoise

        {

            get

            {

                return _myNoise;

            }

            set

            {

                _myNoise = value;

            }

        }

 

        public string MyHappyGesture

        {

            get

            {

                return _myHappyGesture;

            }

            set

            {

                _myHappyGesture = value;

            }

        }

 

        #endregion

    }

}

For my Animal Control I also have the presenter which sets up the default values:

namespace MVP_Example

{

    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:

namespace MVP_Example

{

    public class DogControl : AnimalControl

    {

        private DogPresenter _presenter;

 

        protected override void OnInit(EventArgs e)

        {

            base.OnInit(e);

            _presenter = new DogPresenter(this);

        }

    }

}

Here is my Dog Presenter:

namespace MVP_Example

{

    public class DogPresenter : AnimalPresenter

    {

        private IAnimalView _view;

 

        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:

namespace MVP_Example

{

    public class CatControl : AnimalControl

    {

        private CatPresenter _presenter;

 

        protected override void OnInit(EventArgs e)

        {

            base.OnInit(e);

            _presenter = new CatPresenter(this);

        }

    }

}

Now with the cat presenter, it has its own noise and gesture, so I override those: 

namespace MVP_Example

{

    public class CatPresenter : AnimalPresenter

    {

        private IAnimalView _view;

 

        public CatPresenter(IAnimalView view)

            : base(view)

        {

            _view = view; // get the view from the control

            _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" %>

<strong>animal: </strong><animal:AnimalControl runat="server" />

<strong>dog: </strong><animal:DogControl runat="server" />

<strong>cat: </strong><animal:CatControl runat="server" />

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

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: https://brob.members.winisp.net/MVP_Example.zip (Example was created with VS 2008 Beta 2)

Comments

  • Anonymous
    August 15, 2007
    Hi Brett !Thank you for you nice post. It still leaves me a question,I have a problem: in our project we need to have more then one base classes for pages,1 - base class encapsulates common logic for unlogged user for unloged users, then2- from this class derives some common logic for logged user (translations, process user messages (warnings, errors), encapsulate user data in our IPrincipal (userId, userSettings,…) that are set when user is logged in,…), then3 – base class for a specific user roles in order to build menu, disable some functionalities, etc.So the question is how is better to split that logic using VMP pattern. Create for each base class its own view and its own presenter, OR to derive presenters implementing views… I don’t know I’m confused here… please help.

  • Anonymous
    August 20, 2007
    Well, the way I think of MVP is that the View is your control (such as asp control or win form), the presenter updates your view (and can use the observer pattern) and talks to the Model, the model represents the business objects and logic.  So, I would split up your business logic however you see fit and possibly use some other patterns that are a good fit for your business logic.  The MVP is a good pattern to separate user interaction and business logic.   From what you specified, MVP might work in item 3.  You can use presenters with collections to define what you should display in your view.  For example, if you have your drop down menu and it is dynamic, you could define a List<string> in the interface (that the view will implement), then have your presenter add items to that List<string> object, and then have your View (the control) build the menu based on the List<string> object.  Hope this helps.

  • Anonymous
    August 23, 2007
    Hi again and thank you!My thoughts are:All the business is accessed by the Presenter via Façades, for these façades is separated assembly this is for hide complex business objects. Presenter has reference to façade assembly and the View (Web Project) has access to the presenter logic. I think it’s bad idea to make a reference from View to Façade assembly because after some time we will wake up with a lot of direct calls to facades and ignoring MVP pattern.Base classes as in our case are only part of the View because they encapsulate common logic for the derived Views. For example: ProcessUserMessage(CUSTOMER_DELETED); the function is in base class and is used by derived classes, the function shows a respective message in UI. So I would say that this case for each base class should be a View which derives from the its base view for the Presenters should be the same. Derived classes should be able to access this common logic. Correct me if I’m wrong, Thank you.

  • Anonymous
    August 23, 2007
    Hi Art -What you have described seems correct.  You do not want your UI (view) talking to your facade, only your presenter should talk to the facade.  I personally like to put the controls/views/presenters in a separate library so that they can be compiled and released independent of the website.  I think of it as the View should contain UI specific behavior and the presenters will talk to the model (business layer/facade/whatever) to set the View values.With the common functionality, having a base implementation is a great idea, similar to what I did in my example with the generic animal control.  If your control is only used as a base, you may want to mark it abstract so that it is never used outside of a base context.Just remember that patterns are a good starting point for how to tackle common problems, and they will not fit perfectly in all scenarios.  

  • Anonymous
    August 26, 2007
    Thank you Brett, your comments are very helpfully!I have one more question may be a little bit off topic, but still about MVP, now we have a Data Grid which we bind with a list of objects for example customers through dataSource property and subscribing to OnItemDataBound event, when for each customer the event is raised we can make some additional data calls (via Facades) or do some logic depending on which data it has, for example get number of orders and order lines. How it’s better to split that kind of logic because using MVP OnItemDataBound is in View and we can’t call facades methods directly, also we should leave the View dumb.Thank you again and again.

  • Anonymous
    October 13, 2007
    Hi Brett,a very nice example to understand MVP, hats off to you :)do you suggest any best practices for implementing MVP...

  • Anonymous
    July 01, 2008
    Hi,I want control to be loaded dynamically, what type of control and how many controls information is available in the database. Where can i fit this logic in MVP Pattern.

  • Anonymous
    July 02, 2008
    Hi Prashanth - I'm not quite sure what you mean by control information available in the database. You can load any controls content dynamically, by placing the logic to get the dynamic data in the Presenter layer.  The presenter will set your view controls with the dynamic data.  So, you'll probably want a data access layer that your presenters will talk to in order to set your view controls data.

  • Anonymous
    July 25, 2008
    So the separate animals is pretty easy.  How would you go about creating a chymera (an animal that barks and meows) and reuses the parts of the base?