I’ve been doing some form of application development for at least the past 10 years, and it quickly became apparent to me that in order to maintain your sanity, you need to have some sort of overarching design pattern. Like most developers, my initial projects were a unarchitected mess: that is, business logic was completely mixed in with the presentation logic and UI code.
Eventually I came to the conclusion that it’d be much better to pull out the business logic and data objects from the UI code and have them in a separate library – a partitioned, architected approach to application design. That was a lot better, but the UI code was still very messy.
But I found a solution that I think works the best, so for my most recent projects, i’ve been trying to use the Model-View-Presenter pattern described by Martin Fowler, in either the Supervising Controller or the Passive View variants, depending on the situation.
I won’t get into the details (for that visit the links above), but at the high level, the MVP philosophy is to separate your code into 3 logical areas:
Model – business objects, business logic, services
View – interfaces which each represent a single screen of the application
Presenter – presentation logic for Views that coordinate between them and the Model
If I had to make an analogy, I’d compare the MVP pattern to a car – where the Model is the engine, the Presenter is the chassis and linkages, and View is the body panels, paint, stereo knobs, pedals, and door handles.
What you gain by partitioning the code along these divisions is
* simplicity – by separation of concerns
* portability – via UI-agnostic abstractions and interfaces
* testability – removing UI dependencies and extracting out as much high-level logic as possible means you can now write more automated unit tests and have more coverage
Traditionally, if you wanted to write a mobile app and deliver it to several platforms (say iOS / MonoTouch, Android / MonoDroid, Windows Mobile, and Windows Phone 7), you’d probably have to rewrite significant portions of the app for each platform. Even if the UI worked exactly the same across all platforms, you’d end up rewriting the same presentation logic for each. MVP’s advantage is that you can separate the high-level presentation logic from the view implementation – so you’d be able to reuse the Model and Presenter between all platforms, and only have to rewrite the View. Another major benefit of MVP is that it is also very easy to write tests against the Model and Presenters. The only manual testing that needs to be done is at the View (and even then there’s some automated testing available, at least for WP7).
Should I use MVP for my app?
There are pros and cons for each approach that you’ll have to consider in order to determine if you should use MVP. Obviously any architecture is going to be better than no architecture.
* MVP requires writing more classes – Sure, with MVP there are more classes, and all you’re doing is really shifting the location of the complexity – BUT every class is way smaller and very tightly focused. So there will be more classes but each class will have minimal complexity and be much smaller in scope.
* My app isn’t that big or complex, so I don’t need MVP – This is definitely a valid point, for if you’re writing a one-or-two screen app with no long-term maintenance needs, then it makes sense to take the simplest, fastest approach. But if very rapid development is not a concern, then the good news is that you don’t need a really big and complex app to see the benefits of using the MVP pattern. Even a simple app can benefit from the separation of concerns and enhanced testability. And I will soon release the base classes and MVP framework which will handle all the basic low-level architectural needs and let you worry about application code.
So you’re still interested in MVP? Then let me explain the layers and how I implemented them.
Nothing really to explain here, as this is the area which contains your data objects, business logic, services, DAOs, etc. Be aware that you will have some variations in the Model across platforms, due to platform idosyncracies – but that’s why you code against interfaces and use an Inversion of Control container, right?
Let’s start off talking about the Views – these are the screens that your app displays and the user interacts with. The View interface is the important thing, because that is what the Presenter will be interacting with. An implementation of a View interface can be implemented as a single class, or may require multiple classes. In the case of multiple classes, there will still be only a single class actually coded to the View interface, and that class will act as a Gateway, Event Aggregator, and Window Driver (heck why not just list all of Fowler’s patterns), orchestrating between the supporting classes.
Here’s some general design guidelines about Views (the interfaces, that is):
* it has events that bubble up user interactions like DoCloseThisScreen, DoLaunchWebPage, DoSaveCurrentData, or DoEditChildRecord(id), and are usually fired by the UI in response to a button click, etc.
* these events have very simple signatures such as Action or Action<Guid>.
* all events are synchronous. That is, it shouldn’t asynchronously fire an event – it should be fired synchronously and then it’s up to the listener (the associated Presenter) to do whatever it needs to do asynchronously.
* the View interface does not reference any UI-specific classes like ListBox or UserControl, etc. Every referenced class needs to be UI-agnostic, because the same View interfaces are used across all target platforms.
* it has properties for the data that it will display and/or manipulate.
* it has simple command methods like Show, Close, DisplayConfirmationDialog, DisplayMessage, etc.
I created a base IView interface which all specific View interfaces inherit from, and added in the lifecycle events that I figured were common to every View:
+ Loaded – fired when the View has finished loading. This only happens once per instance!
+ Shown – fires when the View is displayed to the user. This can happen multiple times in a View’s lifetime, for instance if a View is loaded & shown, navigated away from, then back to it.
+ DoClose – fires when the View is attempting to close, and can be cancelled via the passed CancelEventArgs.
+ Closed – fired when the View has been closed (navigated away from).
+ Unloaded – fired when the View has been unloaded (disposed).
These lifecycle events will play a big part in how the Presenter will interact with the View.
Now let’s talk Presenters – the classes that contain presentation logic that control how the application behaves.
I had come from a WinForms background, so I looked at my WinForms dialogs, and extracted all logic that was not specific to the View implementation – that is, presentation logic and calls to business logic. So the Presenters then ended up being a high-level abstraction of a View – and in essence, that is what they are. Presenters are 1:1 to a View interface.
An issue I encountered early on was how WP7 Silverlight handles navigation between views. Since I created Presenters from my WinForms dialogs, they interacted with each other in the same way. For instance, dialogs usually have some sort of return value or work on some sort of supplied data, so it’s common for a dialog to
1) instantiate the next dialog
2) populate properties (or pass via method) with the data to work on or display
3) call a show or display method and
4) wait till that method returns (dialog closes) then work with the modified data.
However, WP7/Silverlight is a very loose, disconnected (and web-like) architecture compared to the highly structured architecture of winforms. Since WP7 apps can shut down and resume at any point in the application, you can’t count on using the method call stack as your app navigation stack. Therefore Presenters have to be highly independent of each other, and each must have enough data on their own in order to know what to do with itself and its data. Presenters call to other Presenters but it can’t wait on data to be returned. It became apparent that presenter-to-presenter flow needed to happen like this:
1) instantiate the next presenter
2) populate properties (or pass into a method) with the data to work on or display
3) call a show or display method and
4) nothing else – it was the responsibility of the called presenter to know what to do with the data.
So the current Presenter must now know and handle everything that needs to be done to the Model – which used to be the responsibility of the calling Presenter. This was probably the biggest conceptual hurdle for me.
So given all of the above, here’s how I have my Presenters designed:
* Presenters are not singletons, and each Presenter / View pair should be their own dedicated instances.
* Presenters do not know or care what the actual implementation of the View is, it only interacts against the View interface.
* Presenters only interact with a single View. Presenters call other Presenters when they want to navigate to another View. The public methods on each Presenter should be some variation on Show (), or ShowData (myData), or EditData (myData), etc.
* Presenters are associated with a View via constructors and Inversion of Control containers.
* Presenters don’t have to have Presenter interfaces. Chances are you’ll never need to have multiple implementations of the same Presenter. Might as well save yourself a few classes and IoC mapping.
* The event handler methods must be written to return control back to the View implementation as quickly as possible. The Presenter must spin off a worker thread if it has to execute a long running process.
Let’s talk about what my Presenters do at each stage of the View lifecycle:
+ Loaded – perform initialization that depends on the View being initialized.
+ Shown – refreshes data from the Model and passes it to the View.
+ DoClose – if on an editing screen, and the data has been modified, ask the user to confirm or cancel the close.
+ Closed – any cleanup to perform when the View has been navigated away from.
+ Unloaded – any cleanup to perform when the View has been disposed (calls the Presenter Dispose method).
Implementing the View in WP7
With the MVP pattern in mind, I set out to develop my first WP7 application, QuickText – and let me tell you it was a fairly intensive effort. A major strength of the WP7 platform is that it leverages Silverlight for applications – but that is also a complication because the standard design pattern for Silverlight apps is Model-View-ViewModel (MVVM), which at first glance doesn’t naturally lend itself to easy integration with MVP. MVVM is what Fowler would call the Presentation Model. Basically, the ViewModel is a container class for all data pertinent to the View, what it displays, and how it behaves (presentation logic). It can also contain Commands, which can make calls to business logic. But the real magic of Silverlight/WPF and MVVM lies in the very simple (but powerful) data-binding capabilities – removing the necessity of writing monotonous and error-prone plumbing code (textbox1.Text = myText, myText = textbox1.Text, etc).
Here’s a rough diagram of how MVVM is architected:
So you can see that MVVM more resembles the extracted business logic layer architecture. It goes further however, by keeping the view state and calls to the model layer in the ViewModel. The PhoneApplicationPage itself should be rather simple, only interacting with the UI controls and view state. MVVM lacks a separated Presentation layer, and that is both its strength and weakness. It is easier to implement, but not as easy to test, and the presentation logic is unable to be reused.
But you can make MVP and MVVM play together, by taking standard MVVM design and extracting the presentation logic and the calls to business logic. All (or most) of the presentation and business logic is extracted from the VM and put into a separate Presenter class. The View data (and view-implementation-specific behavior logic) remains in the ViewModel, which retains all of its databinding goodness. The final result is fully MVP, and a slightly watered down version of MVVM. Let me emphasize that I am not replacing MVVM, but actually integrating it into MVP.
I must admit that the inspiration from this design came from Aviad Ezra and his article on MVP-VM. His MVP-VM pattern is similar to my MVP+MVVM pattern except that his Views directly reference their Presenters and the Presenters directly reference the ViewModel. In MVP+MVVM, the Views do not reference Presenters at all, and Presenters only talk to the View interface and don’t know that it’s ultimately calling a ViewModel.
You can download/view the source code for the PVMP framework on codeplex
v1.5 – 5/2/2011 10:00a – added link to PMVP source code at codeplex
v1.4 – 4/18/2011 10:56p – added the “Should I use MVP?” section, and discussion of MVP-VM
v1.3 – 4/18/2011 4:28p – updated graphics and added clarifications
Leave a Reply