1. Domain logic is not dispersed in the user interface code. When domain logic is strongly coupled with the user interface code, there is a high likelihood of code duplication. Less duplication facilitates debugging, maintenance, enhancements and promotes code reuse.
2. The user interface can be changed independently of the Model in many cases where the application requirements have not fundamentally changed. For example, to enhance usability it may be desirable to change a spin control to a slider control. Similarly, the actual implementation of the Model may be changed to use a different persistence model or to use a smarter algorithm
3. Last but not least, the separation of domain logic from the user interface greatly facilitates unit testing of the code.
Now that we have established the goals and benefits of separating domain and application-specific logic (the Model) from the user interface (the View), let’s delve into how MVP design pattern is useful. It is noteworthy that there are several variations of MVP and its predecessor Model-View-Controller (MVC) design pattern. For the purpose of this discussion, we will let our intuition derive a practical variant of MVP. To begin, we pose this question: Based on the goals and benefits of separating domain and application-specific logic from the user interface, what are some of the desirable attributes of an implementation? We explore this question through a few concrete examples:
1. We want to be able to change the gadgets and controls used on the View without massive refactoring of the code.
2. We want to be able to use a desktop database for one version of our application and a client-server database for another version of our application without having to duplicate too much code.
3. Based on a user, we want to display only certain fields on the View.
4. For an advanced user we want multiple views to show the same information in different ways (for example, a chart and a grid). We want these views to remain synchronized when any of the views are updated.
It is evident from the examples above that we need a degree of separation between the View and the Model. This is where the Presenter comes into the picture. Presenter is essentially the go-between the View and the Model. So what would we expect a Presenter to do? Here are some common tasks suited for the Presenter:
1. Create and display the View
2. Obtain data from the Model and pass it along to the View for display to the user
3. Be aware when a User modifies data on the View. Presenter may need to update the Model or ask the Model for recalculation of some sort. Presenter may need to update some other portion of the View as a result. The View handles basic user interaction (processing of keystrokes to accept data from the user) whereas the Presenter is responsible for making application-specific responses (update the Model and triggering a recalculation).
Now let’s examine some things the players in the MVP implementation must not do:
1. Presenter must not call or manipulate the widgets (controls) on the View directly. We want the ability to swap one implementation of the View with another. If the Presenter called methods on the widgets (controls) directly, this ability will be compromised.
2. View must not manipulate the Model directly. Recall it is the job of the Presenter to marshal the data between the Model and View. This might seem a bit cumbersome but in the bigger scheme of things it is desirable to separate the Model from the View. For example, if there is a need to change the Model later, we will need to change the Presenter and the View code as apposed to just the Presenter code.
We now see dependencies and interactions developing among the players in an MVP implementation:
1. Presenter relies on the View and the Model. Presenter is responsible for preparing and formatting the data suitable for display by the View. The Presenter provides this data to the View through a well-defined interface. The View is responsible for actual visualization of data for the user. The Presenter is also responsible for retrieving any modified data from the View. The Presenter is responsible for preparing and formatting the data suitable to be passed onto the Model for further processing or persistence. Again, the data is passed to the Model through a well-defined interface.
2. The implementation of the View and the Model shall remain independent of the Presenter. To achieve this, we need well-defined interfaces for the View and the Model that the Presenter can talk to. Any View or Model that implements an interface expected by a Presenter can be readily plugged into implementation.
3. The Presenter is also an observer of the events that occur in the View and the Model. Consequently, the View and the Model must publish events that the Presenter can subscribe to. For example, a Presenter may subscribe to a new user data entry event published by the View. Upon receiving notification of such an event, a Presenter may update the Model. A Presenter may also subscribe to data change event published by the Model. For example, if the Model changes due to some outside operation, the Presenter is made aware of the change and can accordingly update the View. Using this mechanism, a Presenter can even manage multiple Views and keep them synchronized.
Figure 1 summarizes the dependencies discussed above.
Finally, let’s put everything together. We begin with construction of a Presenter object. The Presenter object either takes a View and a Model object as arguments in its constructor or instantiates these objects in the constructor. Usually there is a method called Initialize() on the Presenter object that sets the wheels in motion. Presenter.Intialize() creates the View and provides data to the View obtained from the Model. The Presenter then displays the View to the user. The Presenter also subscribes to events of interest published by the View and the Model. Upon receiving such events, the Presenter takes appropriate actions such as updating portions of a View or updating the Model.
Figure 3 shows a sequence diagram summarizing what happens when a user updates data on a View.
The user enters data in one of the controls displayed by the View. The View notifies the Presenter of this data entry. The Presenter asks the View for the updated value. Finally, the Presenter formats this data appropriately and passes it onto the Model for persistence.
Figure 4 shows how the Presenter reacts when the Model is changed externally.
A change is detected by the Model as a result of an outside operation such as an update to the database or an update to the Model by another component. The Model notifies the Presenter of the change. The Presenter obtains the updated data from the Model. The Presenter properly formats the data and passes it onto the View for display to the user. Note if the Presenter is managing multiple Views, the Presenter is in an excellent position to update and synchronize all the Views it is managing.
The apparent disadvantage of using MVP is the extra coding involved in separating the Model, View and Presenter. However, for most applications that involve user interaction, time invested in implementing MVP is well worth the benefits that come with separating domain and application-specific logic from the user interface.