-
Notifications
You must be signed in to change notification settings - Fork 0
(MVP) Model View Presenter
Devrath edited this page Nov 30, 2023
·
15 revisions
To overcome the drawbacks of MVC controller overloading with the business logic. Modifications were done to segregate it to a better representation of architecture called Model-View-Presenter.
Contents |
|---|
| MVP block diagram |
| MVP + Repository Pattern block diagram |
| Using the android architecture component in MVP |
| Code Summary |
- Different people use different variations of the
MVP pattern. - Common and the most popular type of
MVP patternis by using a contract between theviewand thepresenter - The
contractis aninterfacebetween theviewand thepresenter - The
viewcalls a method to initiate an action in thepresenter, the action can be to fetch the data from themodel. - Finally once the
datais retrieved from themodelthepresenternotifies theviewvia thecallback from the contract.
- A
repositoryis used to manage the data fromAPIandloacal database. - We condider
Model=Repository+API+Local Database
- Why not - of course, yes.
- Instead of the
contractthat exists betweenviewandpresenter, We can use aLiveData
-
Common Interface as contract-> We create aninterfaceclass, The interface created has2 nested childreninterfaces.
interface AllCreaturesContract {
interface Presenter {
fun getAllCreatures(): LiveData<List<Creature>>
fun clearAllCreatures()
}
interface View {
fun showCreaturesCleared()
}
}- Through the
Presenterinterface view sends the action to the presenter class.- We can take advantage of the android life cycle component called
LiveDatato update the view class since theLive dataislifecycle aware.
- We can take advantage of the android life cycle component called
- Through the
viewinterface the presenter communicates the callback to theviewclass. - We have the contract implemented in the
concrete classes. - The advantage of having this is when testing we can
mockthe view interface when writing tests for thepresenterclasses. - Since the presenter is
not directly dependentonandroid SDKdirectly, we can write pureunit testsfor thepresenter.
abstract class BasePresenter<V> {
private var view: WeakReference<V>? = null
fun setView(view: V) {
this.view = WeakReference(view)
}
protected fun getView(): V? = view?.get()
}- The
base presenterhas all thecommon codethat all thepresentersneed to be hooked up with theview. - The
base presentertakes the generic viewVthat the base presenter communicates with. - We use the
weak referencebecause the concrete view is anactivity\fragmentclass. We need the views to be properly garbage collected when theactivity\fragmentis properlygarbage collected. - Some examples of
activity\fragmentgetting destroyed is when the user leaves the screen or rotates the device. - We have
getterandsetteradded in the base presenter. - In the setter method, we wrap the view in the
weak reference. - In the getter method, we extract the view from the
weak reference.
class AllCreaturesActivity : AppCompatActivity(), AllCreaturesContract.View {
private val presenter = AllCreaturesPresenter()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(view)
// Set the presenter here
presenter.setView(this)
}
override fun showCreaturesCleared() {
// over-ridden methods of interface
}
}- Implement the view part of the interface from the contract and override the methods
- Have a presenter reference globally
- Initialize the presenter in the
onCreate()method and set the view using the presenter base class setter method
class AllCreaturesPresenter(): BasePresenter<AllCreaturesContract.View>(), AllCreaturesContract.Presenter {
// over-ridden method - action from the view
override fun getAllCreatures(): LiveData<List<Creature>> {
}
// over-ridden method - action from the view
override fun clearAllCreatures() {
// call-back to view
getView()?.showCreaturesCleared()
}
}- Now we
extendthepresenterwithbase presenter - The
base presenterhas a generic parameter where we pass the interface for the view from the contract earlier we set up. - Then we implement interface from the contract where we add the interface and over-ride methods of it so we can get
actionsfrom theview. - To send the callback to the view back from the presenter, we can access the getter method of the base presenter and access the view and defined methods in the view so that overridden methods in view are triggered as the callback.
- To communicate this way, define an interface with certain abstract methods as needed.
- The repository implements the interface and the methods in the
interfaceare over-ridden inrepository. - Pass the repository object in the constructor in the presenter.
class AllCreaturesPresenter(private val repository: CreatureRepository = RoomRepository()) : BasePresenter<AllCreaturesContract.View>(), AllCreaturesContract.Presenter {
override fun getAllCreatures(): LiveData<List<Creature>> { }
override fun clearAllCreatures() { }
}- Using the repository reference call the overridden methods in the repository.
- This enables
unit testingsince we can pass amock repositoryduring the testing instead of a real repository class. - But to send a callback again we can implement an interface in the presenter. Set the interface to the repository with the repository constructor and use it to delegate the callback if required.
- We can have a contract similar to the one that exists between the presenter and view, here also.