Each time I work on a project that incorporates the MVP pattern in some way I always get my head arround the issue what goes where. As we all know a good programmer is the one who can propose good separation of concerns, so the code is readable and understandable by anyone.
Should I put it in presenter or the activity? Where should I manipulate this data that comes from our REST API?
(by the way, be sure to check out the post about mocking REST api responses)
Those are the most common questions I ask myself all the time. In the end, instead of getting rid of god activity objects, most of the times I just shift all the godness into the presenter. The only benefit here is that it’s now easier to test with unit tests instead of UI tests, but still hard to read and maintain. I was trying to figure out how to tackle that problem and finally I’ve came up with the idea of merging few patterns like the Passive View, Presentation Model and MVP together.
That is how the DroidMVP library was born, that, in my opinion, should make it easier to separate certain responsibilities of the app, and make it much easier to maintain the code as a whole. So how does it work?
View — HOW
Activity, Fragment, a custom view — whatever fits your needs. The main responsibility of the View is to display certain state to the user. A progress bar, a text, a dialog indicating successful operation. Everything that is meaningful for the user. The View should know HOW to display certain things, not when, so basically your Activity or fragment (or whatever you come up with) should be treated as a meta-widget composed of Android ones (like TextView or ImageView) with the interface that exposes certain actions, like:
Remember, it’s not the View’s responsibility to determine whether it should shift to certain state or not. So for example, if a user clicks a button that will trigger REST API call, DON’T do this:
instead, let your presenter decide whether the progress bar should be shown or not, so it will look much more like this:
Benefits: by making the view as dumb as possible (that’s what the Passive View pattern is about generally), you shift all the logic away to the classes that are not android-related, therefore you can ommit the UI tests and the risk of not testing your view is minimal (favor unit tests over UI tests).
Presenter — WHEN
Presenter itself is a controller of our view. It shifts the view’s state and handles all the user interaction, delegating the hard work to the domain layer (like REST API or usecase classes). Presenter knows WHEN to display certain states and triggers presentation model updates, while delegating all the work to domain layer. So for example: when a user clicks a button, presenter tells the view to display progress bar, ask the domain layer for a data/update, stores the result in Presentation Model and updates view’s state with the data transformed within the Presentation Model, like so:
As you can see, presenter is responsible of controlling the view state while delegating all the business logic to domain layer and the view logic (the WHAT part) to Presentation Model.
Benefits: By moving all your business logic and presentation logic away from presenter, Your presenter becomes much more clear and easier to read, and while reading the code you can focus on WHEN something should happen, without caring about HOW and WHAT should be displayed.
Presentation Model — WHAT
One of the most important parts of your app is presentation model. It is responsible of holding the current state of your view that is visible to the user as well as transforming your domain model into the presentation model. Presentation Model knows WHAT should be displayed to the user, which is determined by the model updates triggered by the presenter. For example, when the users list is fetched from the API, presenter sets the users list within presentation model and based on that data the model knows if the alert for a user birthday should be displayed or not, and which users are “affected”. The logic is encapsulated within the Model’s public methods:
- Try to make the data stored within the model be immutable. The only one entity that is responsible of mutating data is the model itself, so by making it immutable you won’t provoke any of those using the code to do the mutation outside of the model,
- make your presentation model Serializable, since it will be saved and restored with your activity and fragment (EDIT: your presentation model can now be Parcelable),
- keep your model’s test coverage as close to 100% as possible. Since it does not contain any android specific stuff, it should be easy to unit test. And remember, Test first is much easier than debug after!
Benefits: your view logic is separated from the Android framework and presenter itself, so its much easier to spot the potential bugs and keep your tests easy to write.
All the concepts explained above are easy to accomplish with the little library called DroidMVP which you can find here. It consists of the base classes for your Activities/Fragments (or anything that plays a role of MVP view thanks to DroidMVPViewDelegate) and Presenter.
Dependency Injection friendly
If you wish, you can easly incorporate Dagger dependency injection with the library by providing your own base class extending from DroidMVPFragment or DroidMVPActivity like so: