We tried to use the often praised MVP design pattern in a recent Android project and ... we're sold! In fact, we love it, and wanted to share the love!
Model View Presenter (MVP) design pattern is a great way to decouple application code to simple components. Each component does one specific task, which makes them self contained, easy to reason about, and separately testable. The pattern is especially well suited for mobile development and is quite popular among Android developers. Let's look at each piece separately and see what they are used for.
Models are data sources for your application. They are usually shared singleton instances, each provide their specific type of data. Some examples of Models are data sources for REST APIs, local databases, device sensors and state, etc. The implementations can take care of things like caching, serializing calls, notifications of changes, ... Their public APIs should be thread safe that are allowed to be called from any thread. In order to make it easy to swap real Model implementations with mocked, known state ones, there should be an interface for each Model. Models should not know anything about Views or Presenters, they are at the base of the class hierarchy.
Views are the components that show data and allow user interactions. Views are the simplest pieces of the three - there should not be any application logic in View implementations. They simply display the data they're told to display (although they decide which UI components to use) and report back user interactions like clicks, entered text, and so on. As with Models, there should be an interface for each View as well. Having multiple implementations of a View is often useful - one for Phones, one for Tablets, one for headless testing environment, ... On Android, Views are usually implemented by things like Activities and Fragments. Since pretty much all UI toolkits are single threaded, the Views can also ignore thread safety and require that their methods are only called from the application's UI (main) thread. Views do not communicate directly with Models, they only talk to Presenters. Additionally, as View implementations are usually native components of the OS, they are the entry points of the application and thus are responsible for creating Presenters (as many types of Presenters per View as needed).
Presenters are the core logic of the application. They request data from Models, split and format the data, and tell Views what to show. They also decide how to handle user interactions from Views. A Presenter instance has one or zero bound Views - OS UI component life cycles are often quite complex, so View instances can come and go. In order to handle that, Presenters should cache the data, so that it's readily available once loaded, no matter how many times the View is recreated. Since communications with Views are required to take place in UI thread only, it makes sense to require all Presenters' cached data access also from UI thread only. If a Presenter needs to load data from a background thread it forwards the results to the main thread and updates the cached state and View from there. Presenters can also monitor Models for updates and keep their cached data and the bound Views in sync. As such, Presenters usually require Views to inform them when they are no longer needed, so they can stop at the appropriate time.
Some notes on the diagram
- A View can have one or more Presenters.
- A View communicates only with Presenters, never directly with Models.
- A Presenter can have one or zero bound Views.
- A Presenter can communicate with multiple Models.
Testing MVP applications
As the code is decoupled, it becomes easy to separately test each piece. Views can be told explicitly what to display and verified that UI events are propagated to Presenters. On Android, Espresso testing framework can be used for View tests.
Presenters can be tested with help of mocked View and Model implementations. The tests should verify that expected Model methods are called and Views are asked to display the data from Models. Since Presenters can not contain any OS specific code, native unit tests can be written (which is an important property for Android testing, as junit can be used).
Models can be tested by providing mocked data sources (pre-populated databases, mocked HTTP servers, etc), making sure the right calls are made and the data is correctly parsed.
As MVP pattern decouples UI components (Views), in theory it should be easy to share code between Android and iOS clients. Models and Presenters can be shared while each OS implementation provides their native View implementations. Unfortunately, that would require Models and Presenters to be written in a programming language that is available on each OS and need data marshalling between native and shared code. But at least it would be quite straight forward as it would only be needed in communication between Views and Presenters.
Another option would be to define interfaces for each MVP component and share these as guidelines for each implementation. As an example, a concrete View interface "UserProfile" might need to display the logged in user name, its Presenter must have methods to change password and log out.