The Model-View-Presenter (MVP) Pattern

Context

A Web page or Web Part in a SharePoint application contains controls that display application data. A user can modify the data and submit the changes. The Web page or Web Part retrieves the data, handles user events, alters other controls on the page in response to the events, and submits the changed data. Including the code that performs these functions in the Web page or Web Part makes them complex, difficult to maintain, and hard to test.

Objectives

  • You want to maximize the amount of code that can be tested with automation. (Views are difficult to test.)
  • You want to separate business logic from user interface (UI) logic to make the code easier to understand and maintain.

Solution

Separate the logic for the visual display from the event handling behavior by putting them into two classes that are named, respectively, the view and the presenter. The view (the Web page or Web Part) manages the controls on the Web page and forwards user events to a presenter. The presenter contains the logic to respond to the events, updates the model (both the business logic and the application data), and alters the state of the view.

To make the presenter testable, define a view interface and have the presenter refer to the view interface instead of to the view implementation class. This allows you to replace the actual view with a substitute implementation for unit tests.

There are many variations to the MVP pattern. The Partner Portal application uses the Supervising Presenter approach (this is also named Supervising Controller).

In Supervising Presenter, the view interacts directly with the model to perform any simple data binding that can be declaratively defined, without the presenter. The presenter updates the model. It only changes the state of the view when complex UI logic that cannot be declaratively specified is required. Examples of complex UI logic might include changing the color of a control or dynamically hiding or showing controls. The Partner Portal application has many examples of MVP that generally follow the Supervising Presenter pattern.

The following diagram shows the logical representation of the Supervising Presenter approach.

Supervising Presenter

Ff649571.354b3e51-0023-46a2-9288-4cf5744594f0(en-us,PandP.10).png

Compared to other variations of MVP, such as Passive View, the Supervising Presenter pattern makes simpler code a higher priority than complete testability. The Supervising Presenter pattern requires less code than other MVP patterns because it uses data binding. The code is easier to maintain because simple UI changes do not require code changes in the presenter to update the view. For a comparison of passive view and supervising presenter approaches, see Model-View-Presenter.

Note

Most descriptions of the MVP pattern describe how the model can notify the view or the presenter if something changes in the model. This behavior is usually relevant for rich-client applications so it is omitted here. In rich clients, the model is often kept in memory. As the user interacts with the application, the model is updated and it notifies the UI. In a Web application, this scenario is unlikely because of the stateless request/response nature of Web requests. Most of the time, the information is re-retrieved by the presenter from the model for every Web request. This usually eliminates the need for the presenters or views to be updated based on changes to the model.
If you need presenters and views to be notified of changes to the model, you can implement the Observer pattern. For more information, see Related Patterns.

Implementation Details

An application usually has a model in place that represents the domain business logic. You can assume this for the purposes of the following example.

The first step in implementing the MVP pattern is to create a view. Typically, you want to create an interface for the view so that you can replace it with a test implementation. The following code is an example of a simple view interface that retrieves and displays parts for a product.

    public interface IRelatedPartsView
    {
        IEnumerable<Part> Parts { get; set; }
        void DataBind();
        string ErrorMessage { get; set; }

    }

The view is implemented by a user control's code-behind. This is shown in the following code.

public partial class RelatedPartsControl : System.Web.UI.UserControl , IRelatedPartsView
    {
        private RelatedPartsPresenter presenter;

        public RelatedPartsControl()
        {
            presenter = new RelatedPartsPresenter(this);
        }

        // ...

        public IEnumerable<Part> Parts
        {
            get
            {
                return this.PartsRepeater as IEnumerable<Part>;
            }
            set
            {
                this.PartsRepeater.DataSource = value;
            }
        }

        // ...
        
        public string ErrorMessage { get; set; }

        protected void LoadPartsButton_Click(object sender, EventArgs e)
        {
            this.presenter.LoadParts(this.Sku);
        }
    }

Because the view directly constructs the presenter, it is bound to the presenter implementation. This is generally acceptable because the view is a relatively simple class that will not be tested, and the presenter is unlikely to be replaced. The view changes either when the presenter sets information on the view or when events are raised as a result of user actions. An example of the first case is when the presenter sets the Parts property. An example of the second case is when a user selects the LoadPartsButton. This action forwards the event to the presenter by calling the LoadParts method. In either case, all the business logic is located in the presenter.

Partner Portal Application and the MVP

The Partner Portal application demonstrates two variants of the Supervising Presenter pattern. In one variant, a user control is contained within a Web Part. It is the user control that implements the view. The view constructs the presenter internally and calls methods on the presenter. This means that the presenter is hidden from the Web Part. The advantage to this approach is its simplicity, because the Web Part has no need to know anything about the presenter. The disadvantage is that any data that the Web Part wants to forward to the presenter must be forwarded through the view.

The PartnerRollupWebPart class demonstrates another variant on the Supervising Presenter. Here, the Web Part contains a user control to implement the view, and it also interacts directly with the presenter. The PartnerRollupWebPart instance constructs the view and passes the view into the presenter's constructor. The PartnerRollupWebPart instance can then set properties and call methods that are related to the business logic instead of calling these methods or properties on the view and having the view pass them to the presenter.

The following diagram shows the two Supervising Presenter variants. The solid lines represent the case where the Web Part does not interact directly with the presenter. The solid lines together with the dotted line represent the case where the Web Part does interact directly with the presenter.

Two variants on the Supervising Presenter pattern

Ff649571.73e4acf2-ec39-4e21-a955-20d3e623c947(en-us,PandP.10).png

Typically, user controls are the basis for developing Web Parts during design time. This means that the view logic is more commonly used in the user control and the user control is contained in the Web Part. In the Partner Portal application, the Web Part gathers context that is then passed either to the view (an instance of the user control) or directly to the presenter. In the case where the Web Part does not contain a user control that implements the view, the Web Part implements the view interface and directly interacts with the presenter.

The presenter only interacts with the view interface. This decouples the presenter from the view implementation. The view is passed into the presenter when the presenter is constructed. This approach is sometimes referred to as constructor injection. Because the view is passed to the presenter, you can also provide a mock view for unit tests. The following code demonstrates the constructor injection approach.

public class RelatedPartsPresenter
{
  IRelatedPartsView view;

  public RelatedPartsPresenter(IRelatedPartsView view)
  {
     this.view = view;
  }
        
  public void LoadParts(string sku)
  {
     try
     {
        IProductCatalogRepository productCatalogRepository =
           SharePointServiceLocator.Current.GetInstance<IProductCatalogRepository>();

        IEnumerable<Part> parts = productCatalogRepository.GetPartsByProductSku(sku);
        if (parts != null && parts.Any())
        {
           view.Parts = parts;
        }
        else
        {
           view.ErrorMessage = Resources.NoPartsFoundError;
        }
        view.DataBind();
     }
    // ...
  }
}

The presenter retrieves the model (this is the list of parts) from the product catalog repository. The presenter then sets the model into the view and tells the view to perform a DataBind operation. The code also demonstrates the use of the Service Locator pattern to retrieve the repository. The service locator improves the application's testability. For more information, see The Service Locator Pattern.

Considerations

There are several issues to consider if you want to use the MVP pattern:

  • There are more solution elements to manage.
  • You need a way to create and connect views and presenters.
  • The model is not aware of the presenter. If the model is changed by any component other than the presenter, the presenter must be notified. Typically, notification is implemented with events. This is not a common scenario in a Web application.

The MVP pattern in the Partner Portal and Training Management applications is primarily for unit testing. An added benefit is the ability to separate each type of logic into its own layer. Both applications combine the MVP pattern and the Repository pattern with a software development technique named unit testing with mock objects. This technique breaks the tests' dependencies on external components, such as SharePoint. The mock objects emulate SharePoint behaviors during the unit tests. Mock objects substitute for views and repositories during the unit tests.

For more information about mock objects, see Martin Fowler's article Mocks Aren't Stubs. For guidance about unit testing, see Unit Testing with Mock Objects in this documentation. For a discussion of the MVP pattern, see Derek Greer's discussion, Interactive Application Architecture Patterns.

Related Patterns

  • Application Controller. If presenters interact with an application controller, the presenters do not need page flow and screen navigation logic. This makes it easier to update the page flow.
  • Model-View-Controller (MVC). The MVP and MVC patterns have similar goals, although there are differences in how they achieve those goals.
  • Observer. In this pattern, one object can observe changes on another object without referencing the implementation of the object being observed.

Home page on MSDN | Community site