What is MVP All About?
Model-View-Presenter is a design pattern for separating the concerns of display and display coordination when interacting with an underlying object model / domain. Better put as Model, Presenter, View due to the sequence of connections that are actually involved, the paradigm can be broken down as:
- Model - The observable data model that is the subject of the user-story.
- Presenter - A construct that takes data from the model and exposes it to views. Also responsible for taking updates from views and re-applying them to the model.
- View - The display portal for the system. Shows the interaction and display surface of the application.
Arguably a variant of MVC (Model View Controller), where a controller acts as an external mediator, the MVP pattern is distinct in that it puts the model further out of reach of the display implementation. Let's, for arguments sake, call the 'Presenter' aspect of MVP a Controller, and compare and contrast the roles of each entity in the different paradigms:
Element
| Model-View-Controller
| Model-View-Presenter
|
Visually
|
|
|
Model
| Application domain logic
| Application domain logic
|
View
| Receives model elements from controller. Displays and interacts with element.
| Receives data from presenter, notifies presenter of interactions.
|
Controller
| Supervises view/model, performs actions in response to events/changes.
| Delegates events to model. Re-maps model outgoing events into state changes.
|
Each technique has its own merits. Martin Fowler identifies MVP as a 'Passive View' variant of Model-View-Controller. In more traditional MVC, a view would receive an update and manipulate the model accordingly after being configured by its controller. In Passive View or MVP, a view notifies its presenter of the update and the presenter then makes the corresponding model changes on its behalf, acting as a bi-directional mapping layer.
Existing MVP Implementations
As with anything when it comes to design patterns, a lot of people have had a stab at Model View Presenter over the years, including versions for Microsoft's .NET Framework. Because of the complexities of bi-directional abstract communication (for the presenter/view coordination), most implementations typically leave only one side of the system abstract: views.
This choice forces a few undesirable traits on us, but the key one is that you can't mock out the presenter or swap the implementation entirely. If the view needs to notify the presenter of changes, it needs to be coded against the specific view type.
It's often argued that this approach is just fine when you're building things like web applications, since you rarely want to test your view implementation, and instead people tend to focus on testing presenters with mock views. Some developers aren't so comfortable with this though, and often there is need to mock out the presenter and allow the views to be built separately or even built mostly up-front (as will often be the case when working with design agencies).
Abstracting the Presenter / Views
So far we've abstracted as far as traditional MVP indicates, but this leaves some purists with a bad taste. We've only really made a third of the stack 'abstract' in any traditional sense - and our abstract views are hard-wired to the presenter implementation, which in turn is hard-wired to a model implementation. We can solve the latter problem by using traditional interfaces to abstract away our dependency on the model, but what about the presenter itself?
Ideally, we want to end up in a situation much like:
The presenter works against the model, interacting with views via an interface. View implementations in turn receive updates and work against a presenter interface.
Coupled Contracts with .NET Generics
One of the abilities of the generics syntax in the .NET Framework is the ability to create self constrained generics, that is, generics that make some guarantee about themselves. We can use this to produce an interface definition for both views and presenters that are strongly typed but bi-directional!
public interface IView<TViewContract, TPresenterContract>
where TViewContract : IView<TViewContract, TPresenterContract>
where TPresenterContract : IPresenter<TPresenterContract, TViewContract>
{
void AttachToPresenter(TPresenterContract presenter, bool requiresInitialState);
void DetatchFromPresenter();
TPresenterContract Presenter { get; }
}
public interface IPresenter<TPresenterContract, TViewContract>
where TPresenterContract : IPresenter&<TPresenterContract, TViewContract>
where TViewContract : IView<TViewContract, TPresenterContract>
{
void ConnectView(TViewContract viewInstance, bool requiresState);
void DisconnectView(TViewContract viewInstance);
}
This seemingly odd generic syntax allows us to now have a bi-directional coupling between instances that are exclusively interface driven, giving us the ability to replace either end of the agreement and not sacrifice any strong typing! A presenter can maintain a strongly typed collection of views, but at the same time, the view can maintain a strongly typed presenter property and other facets.
To use these interfaces, simply declare a contract pair of the form:
public interface ISomeView : IView<ISomeView, ISomePresenter>
{
}
public interface ISomePresenter : IPresenter<ISomePresenter, ISomeView>
{
}
The corresponding classes that implement these interfaces will have strongly typed mechanisms to enable inter-object communication.
Example - Tick, Tock, Model-View, Clock?
A good starter for ten when demonstrating the unyielding impracticality of a design pattern is the 'Clock' scenario. Keeping with the established tradition, the attached project includes an example application suite that implements a clock using Model-View-Presenter conventions.
The model is a 'TimeModel' that maintains the current time (and fires an event periodically to indicate the time has changed!). The presenter connects to this model's events and notifies its view of any corresponding change in time. Views, in turn, are notified of this, and update themselves, but also give the user a means to change the time-zone. When a user decides to shift time-zones, the presenter is alerted by the view, and the corresponding change is made to the model.
As a consequence of this, an event is raised on the model; the subscribing presenter then pushes the information out to each of its views. As a sequence diagram, this is roughly the scenario:
The relationship between the various libraries in the example project is as follows:
The application could use Unity or any other Inversion of Control mechanism to perform the activation and connection of the pieces of the puzzle. However, rather than turn this article into a tutorial on the Enterprise Library, I'll leave that one to the reader!
In terms of specific implementation - the orchestrator element in the example application is a simple Windows form, the model and presenter are both conventional classes. The presenter inherits from a base presenter class that deals with many of the more tedium prone aspects of implementation (such as managing a collection of connected views, etc.).
The view itself is implemented as a WinForms UserControl. and shown twice on the form, and looks like this:
When you change the time-zone of one view, the other also receives the update (through the coordination logic of the presenter, acting in response to model changes). The data exchange is possible because of the contract coupling.
And there you have it. A Model-View-Presenter pattern with bi-directional strong typing of communication, but also abstracted from concrete classes.
Finishing Thoughts - Why the Coupled Contracts?
I've been asked before why I've opted for the mutually guaranteed contracts - doesn't it require that a specific presenter have a specific type of view? What if you want to build a completely different presenter and have it use the views?
For these situations, it's possible to use inheritance on the view/presenter interfaces so that your presenter can add additional operations. Interfaces also allow for multiple inheritance, so a presenter can actually support multiple types of views with the same reliable pattern.
If you wanted to change this pattern to enable 'Active View', rather than 'Passive View', the only change needed is to have the model interface passed through to it by the presenter. This would allow the view class to interact with the model directly, but still allow the events to bubble through to the presenter and cause the view to receive corresponding updates.
Next Time - ASP .NET Orchestration with MVP?
For my next article, I'll be covering how to use this pattern with ASP.NET, but not have to resort to tons of code-behind in order to perform the linking between views, presenters, and models.
Peace!
History
- 10 October 2009 - Initial draft (+ Update to fix formatting oddities that made the generics bit look odd).