MVP (design pattern)
During my last job interview I was asked about the MVP pattern.
Why this question?
It does makes plenty of sense to ask this particular question during a job interview. If you do not know any viable alternative to MVVM, then how do you know why you are using the MVVM pattern in the first place?
Please note I am not very good at implementing the MVP. The MVVM and LiveData
are my thing. Still, the MVP may be useful when you do need to invoke some code directly on the view. The presenter doesn’t need to know about the view, though. You only need to pass to it the actual lambdas you are going to invoke.
I wrote the example project presented in this article as a refactoring of another project to prove that - at least to some extent - I understand the difference between the MVP and the MVVM.
The project
The project used in this article is MVP. It is a refactoring of MVVM.
Please read the previous article to understand the original architecture.
As I was refactoring the project, I changed the package name from pl.org.seva.mvvm
, deleted the package viewmodel
and created a new package presenter
. In the new version of the project I do not use the Android’s class ViewModel
nor LiveData
at all.
The presenter
This is the code of the presenter that subscribes to the RxJava’s Observable
described in the previous article, interacts directly with the view and handles the lifecycle:
The presenter really only needs to keep references to lambdas that are a part of the view. It doesn’t need to know more about the view:
What the build()
function does is the following: it collecs the lifecycle from the LifecycleObserver
, creates a Disposable
by calling subscribe()
on the Observable
, and eventually disposes it.
The crucial line is:
Because the presenter deals with the view, it can only do so on Android’s main thread.
If the view happens to be paused, the above code will store the value last generated by the Observable
, but it won’t present it until the view is resumed. Notice this line:
I cannot replace it with:
The value of last
is updated from another thread, so it can be changed to null
even after the check if (last != null)
has already been made. Calling last?let
captures the most up-to-date value of last
, and if it is not null
then executes the block.
In the Fragment
This is what it takes to start observing activity recognition in the Fragment
, using the above extension function:
I use the following extension function:
Calling build()
not only creates an instance of ActivityPresenter
but also starts observing the lifecycle of the receiver (here: LifecycleObserver
).
Altertatively, I could implement one function build()
to just create an instance of ActivityPresenter
, and then write a separate function, for example observe()
, to start observing the lifecycle. Creating two different functions would promote separation of concerns, but I decided to stick to only one function in order to promote a more concise code. Your particular architecture choice is up to you.
Testing
Testing is not different at all from the techique discussed in the previous article. Because testing doesn’t depend on choice of architecture (MVVM or MVP) you can use identical code.
Please note, though, that because I changed the package name, you also have to change the testInstrumentationRunner
in your module-level build.gradle
:
Conclusion
In this article I described the way in which you could refactor the MVVM architecture - described in the previous article - to the MVP alternative.
I do not exactly understand the advantages of one over the other, but I do not personally fancy handling the lifecycle manually, so I will stick to the MVVM, even thought I hope to have proven that switching from one to the other is trivial.