MVCPH (Move PresCon to Hel)
MVPCH is Model View Presenter Controller Helper. What is that ? That not a concept or pattern. That is something in my mind when i want to create clean structured code and how i separate them. So what is that actually ?
nb: i’m using android concept to explain this because i found this when code on my work and personal project.
M = Model, is model (pojo) like User (ex: name, birth date, gender, etc)
V = View, is a layer to show the UI to the user (ex: activity, fragment, custom view)
C = Controller, is a layer that have your business logic that will process your model to view. But i’ll no direct pass the model to view, why ? because we’ll might be not need all data to pass to view and we also need to process the model like parsing, formatting, calculate and etc. (ex: LoginController, TweetController, etc)
P = Presenter, is a layer act as bridge to communicate from Controller to View. Presenter will be like an interface inside the controller. It have specific parameter on every function. If we need different parameter, we need to add another function. (ex: LoginPresenter, TweetPresenter)
Every controller must be responsible for one presenter so the solution is create a nested interface inside the controller.
H = Helper, is a Utility class like a hammer and chisel to create a statue. If your model is want to process like you have birth date and want to get user age, if you want to wrap an image load third party library, and helper to connect, get, and write data to database. Why ? android is having much third party library which is every library have different flow. And if we want to migrate from 1 library to another imagine how much code you need to replace (horrible right? ) (ex: ImageHelper, UserHelper, DatabaseHelper, etc)
code example and explanation :
Model :
public class User {
public long uid;
public String name;
public String gender;
public long birthdate;
public long analyticsId;
}
model is attribute of user that defined and describe a user. Are we need to synchronize structure user model in local and server user model ? That’s good question. I will answer 2 different answer as my opinion :
YES, because we need to keep structure of business needed. But what if there is field that we don’t need ? hmmmmmmm. Look at analyticsId in our User class, are we need it ? NO. So get rid of it.
-
NO, the answer is in option 1. BUT REMEMBER THE SINGLE RESPONSIBILITY PRINCIPLE do not do this :
public class User {
public long uid;
public String name;
public String gender;
public long birthdate;
public boolean endGame;
}
View :
Yes, its like customer service that face our user. Like the concept of customer service, we just serve the user with something useful to user. Do customer service know how to get data from database ? HELL NO. Do customer service know how to get saving from a web service ? HELL NO. Customer Service just need to communicate to user and process the specific information. So do the UI, UI don’t need your Networking library code, UI don’t need to know how to query the database, what UI need to do is like customer service, just the abstraction of functionality.
Example :
public class ViewUserInformationActivity implment UserControl.Presenter {
UserControl userControl;
Button btnUpdateUser;
@override protected void oncreate(Bundle saveInstanceState) {
super.oncreate(saveInstanceState);
setContentView(R.layout.user_activity);
userControl = new UserControl();
int userId = 1;
userControl.loadUser(userId);
btnUpdateUser.setOnClickListener(new View.OnClickListener) {
userControl.updateUserName(txtName.getText().toString());
}
}
@override public void showUserData(String name, String address, int age) {
txtName.setText(name);
txtAddress.setText(address);
txtAge.setText(""+age);
}
}
as you can see, view just care how to showUserData to user. It doesn’t care the model of user, how to calculate user page. Is the view care if address is empty or null ? thanks to Java we have String.valueOf. So, where we place the String.valueOf ? yes you are right put it on Controller.
Controller :
Yes, its function to handle your business logic.
public class UserController {
User.Controller mPresenter;
public UserController(UserController.Presenter presenter) {
this.mPresenter = presenter;
}
public void updateUserName(String name) {
ApiService
.getInstance()
.updateUser(name)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(user -> {
saveUserToDb(user);
presenter.showUser(user.name, user.address, UserHelper.getAge(user.birthdate));
});
}
public interface Presenter {
public showUser(String name String address, int age);
}
}
so, thats how controller look a like. It doesn’t care how user attribute display in UI. And yes, it’s make this controller more testable.
Presenter :
What presenter is ? presenter is a communication bridge from model to view. All Presenter is implement in view, and every controller is responsible for every presenter that define inside the controller. Look at code above the interface presenter is implement in activity.
Helper :
So, we have controller and what is the responsibility of helper ? In android you have many powerful third party library Picasso, Glide, Fresco, Universal Image Loader. So what if when we are using Picasso, but unfortunately we want to test another library as our image loading ? Yes the naive solution is to find and replace all code. Is there any simple way ? yes by create a ImageHelper.
This is the code when we are on Picasso,
public class ImageHelper {
public static void loadImage(Context context, ImageView imageView, String url) {
Picasso
.with(context)
.load(url)
.fit().centerCrop()
.into(imageView);
}
}
When we are want to move to Glide :
public class ImageHelper {
public static void loadImage(Context context, ImageView imageView, String url) {
Glide
.with(context)
.load(url)
.into(imageView);
}
}
Other responsibility of Helper is ModelHelper. For example if User is have birthdate, how we calculate user age ? This is just example. (forget the logic to calculate the age)
public class UserHelper {
public static int getUserAge(User user) {
int age = System.getCurrentTimeMillis() - user.birthdate
return age;
}
}
And you can also do this for database, like if you are using ORM, Realm or ContentProvider :
public class DatabaseHelper {
public static User getUser(int id) {
User user = query.whereId(id);
return user;
}
}
So the purpose of all this is so the code that we make is testable based on their each functionality. Next part i will explain about my thought about the
testing.
Any feedback or correction ? feel free to email me : hi@hasibuan.me