Refactoring an Activity into MVP with Kotlin
A short time ago I created a simple Activity
for a colleague to give me a hand with an easy feature that displays a list of information. He wrote the feature in the same Activity, as traditionally it’s been done, not using any MVP design, so I’m going to refactor this code step by step.
Note 1️⃣: I’m following Uncle Bob Clean Architecture so a feature should be divided in ui
, domain
and data
. This article treats the ui
part which will be divided in model
,view
and presenter
Note 2️⃣: We’re not going to show any layout here as it’s not relevant with the topic.
Code I gave to my colleague
I like creating companion objects to start activities from outside and remove inner logic externally.
Feature done by my colleague
I’m going to show the code it was given to me which is 100% working:
First of all, I have to agree that using ListView
is a bad option having RecyclerView
from the support library, so I’ll change ListView
to RecyclerView
and its respective Adapter
💁🏻. I’ll be showing only the code that regards the SimpleListActivity
but the same procedure is done with the RecyclerView.Adapter
and the RecyclerView.ViewHolder
First step is cleaning code and separating functionalities
Explained in another way, I want to separate the activity in simple tasks, for example:
- If we configure a Toolbar
we create a function specifically for that.
- If we set many click listeners, we group them in the same place.
- We will also remove also unnecessary code.
- We will pass the parameter pages
to the Adapter
. The Activity
doesn’t have to know about it.
- We will separate the Adapter
from the SimpleListActivity
keeping them all in the same package simpleListActivity.ui.view
One of the reasons why MVP was created is to separate logic from the Activity and place them somewhere else. Now, if we pay attention to this SimpleListActivity
it does basically:
- Set OnClickListeners
- Loads a BannerImage
- Configures a RecyclerView
So our SimpleListActivity
will just know about that, while the presenter will compute most of the logic
So far, the refactored code would look like this. This doesn’t have any MVP yet, don’t rush it! 🏃
Time to create the View Presenter
I call the View Presenter to the interface that will go with the SimpleListActivity
and the one in charge of changing the views with really simple logic or with a logic that the Logic Presenter carries on. Let’s add an interface called SimpleListActivityViewPresenter
insimpleList.ui.presenter
What is this activity changing in terms of VIEW?
- It’s just refreshing views (related to RecyclerView
and another layout)
- It’s loading a Banner Image
- It’s setting Click Listeners:
.- When back is clicked
.- When signIn is clicked
.- When register is clicked
So those will be our functions in our ViewPresenter:
SimpleListActivityViewPresenter.kt
I created a getActivity()
method to simply get the Context
but it can be done in many ways
Time to create the Logic Presenter
I call the Logic Presenter to the interface and class that will be added as a variable that will compute the logic within the View Presenter Let’s add an interface called SimpleListActivityPresenter
and a class called SimpleListActivityPresenterLogic
in simpleList.ui.presenter
What logic are we using here?
First of all, every override
function we use that has a logic should be added in the LogicPresenter, plus every single logic we compute, such as when back button is clicked.
The result should look like this:
SimpleListActivityPresenter.kt
SimpleListActivityPresenterLogic.kt
SimpleListActivity.kt
as you can see now, our SimpleListActivity
has a short length, and it describes what it does (creates, resumes, loads banner, configure recyclerViews, and declares its views). The rest is done by the SimpleListActivityPresenter
which carries on with the SimpleListActivityPresenterLogic
to do all the “ugly” code. I believe it involves more code in different files, but it helps more to understand every single Activity and helps also to do UI testing.
Our MODEL
Unfortunately, for this example, the list is based only on String
but that is already a native class, so the simpleList.ui.model
would be empty in this case. If a more complicated object should be needed (for example for the Adapter
) that model should go there, never a model coming from the server.
What do you think?