以前查看別人的時候會看不懂別人的項目結(jié)構(gòu)犁珠,里面有個 presenter袱蜡,其實代碼勉強看得懂丝蹭,但是邏輯會感覺很混亂,所以后來學(xué)習(xí)了下 MVP 模式坪蚁,現(xiàn)在需要整理一下奔穿。
MVC 和 MVP 的區(qū)別
說起 MVP 模式不得不提起 MVC 模式,也是我們接觸最多敏晤,也應(yīng)該是最早接觸的贱田,雖然一開始的時候感覺自己寫的連 MVC 模式都談不上??。
MVC 模式的全程是 Model-View-Controller
- Model 業(yè)務(wù)邏輯和實體對象
- View 布局文件
- Controller 控制器嘴脾,也就是常說的 Activity
MVC 模式在小型的項目上面很適用男摧,說白了就是簡單粗暴蔬墩,邏輯簡單,View 就是布局耗拓,實際的關(guān)于數(shù)據(jù)綁定的工作都是交給 Activity 來完成拇颅,這樣子搞得 Activity 又像 Controller 又像是 View,借用網(wǎng)上的圖來表示這種關(guān)系
也就是說由于 Activity 的職責(zé)很模糊乔询,就會導(dǎo)致 Data 和 View 的關(guān)系很亂樟插。
而如果換做是 MVP 模式的話,關(guān)系則就會清晰很多
其中 MVP 代表的是 Model-View-Presenter竿刁,其中最大的特點就是 Presenter黄锤,它是 Model 和 View 之間的橋梁,它們分別的作用如下所示:
- View 對應(yīng)著Activity们妥,對應(yīng)著 View 的繪制和用戶之間的 ui 交互
- Model 和在 MVC 里面的作用一樣是業(yè)務(wù)邏輯和實體對象
- Presenter 負責(zé)完成 View 和 Model 之間的交互。
和上面的圖描述的一樣勉吻,接下來的一張圖則更加明了监婶,是一張很經(jīng)典的描述兩種模式區(qū)別的圖
總結(jié)
MVP 減少了 Activity 要做的事情,簡化了它的代碼齿桃,把許多事情放到 Presenter 里面去完成惑惶,模塊劃分清楚,層次清楚短纵,從而降低了耦合(這里的低耦合带污,我通俗的理解就是在 MVP 模式里面,View 和 Presenter 之間香到,Presenter 和 Model之間可能互相都持有引用(這里錯了鱼冀,是只能 Presenter 持有 Model),但是 View 不會持有 Presenter 的引用悠就,反之也沒有)千绪。
例子分析
這里我們拿一個登錄的例子來說這個問題,首先我們先看下這個項目架構(gòu)梗脾,是不是感覺分的很清晰荸型,Model 層,Presenter 層和 View 層都分的很清楚炸茧。
Model 層實現(xiàn)
userBean 類
public class UserBean {
private String userName;
private String password;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User(" + "\n" + "userName = " + userName + "\n" + "password = " + password + ")";
}
}
定義的 LoginModel 接口規(guī)定 Model 類要實現(xiàn)的功能瑞妇,當(dāng)然是登錄,但是這里要注意的是這里面使用的 OnLoginFinishListener 這個接口是 Presenter 里面的梭冠,通過它辕狰,Presenter 才能知道是否登錄成功。
public interface LoginModel {
void login(UserBean userBean, OnLoginFinishListener onLoginFinishListener);
}
接著是 LoginModelImpl 這個 LoginModel 的實現(xiàn)類
public class LoginModelImpl implements LoginModel {
@Override
public void login(UserBean userBean, final OnLoginFinishListener onLoginFinishListener) {
final String userName = userBean.getUserName();
final String password = userBean.getPassword();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
boolean error = false;
if (userName == null || userName.isEmpty()) {
onLoginFinishListener.onUserError();
error = true;
}
if (password == null || password.isEmpty()) {
onLoginFinishListener.onPasswordError();
error = true;
}
if (!error) {
onLoginFinishListener.onSuccess();
}
}
}, 2000);
}
}
View 類的實現(xiàn)
LoginView 接口控漠,定義 Acitivity 要完成哪些交互柳琢。
public interface LoginView {
void setUserError();
void setPasswordError();
void onSuccess();
}
接著是這個接口的實現(xiàn)類,在這里面實例化一個 Presenter 并持有它,通過 presenter 的實例對象來完成具體的登錄和 onDestroy 里面的釋放操作柬脸,釋放掉對 view 的持有他去,也就是 activity 的持有(這里涉及內(nèi)存泄漏的知識)。同時也要實現(xiàn)一些 ui 的交互倒堕,也就是 LoginView 接口里面定義的方法灾测。
public class LoginActivity extends AppCompatActivity implements View.OnClickListener, LoginView {
private EditText userEditText;
private EditText passwordEditText;
private Button loginButton;
private LoginPresenter loginPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
userEditText = (EditText)findViewById(R.id.et_user_name);
passwordEditText = (EditText)findViewById(R.id.et_password);
loginButton = (Button)findViewById(R.id.btn_login);
loginButton.setOnClickListener(this);
loginPresenter = new LoginPresenterImpl(this);
}
@Override
protected void onDestroy() {
loginPresenter.onDestroy();
super.onDestroy();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_login:
UserBean userBean = new UserBean();
userBean.setUserName(userEditText.getText().toString());
userBean.setPassword(passwordEditText.getText().toString());
loginPresenter.validateUser(userBean);
break;
}
}
@Override
public void setUserError() {
Toast.makeText(this, "用戶名錯誤", Toast.LENGTH_SHORT).show();
}
@Override
public void setPasswordError() {
Toast.makeText(this, "密碼錯誤", Toast.LENGTH_SHORT).show();
}
@Override
public void onSuccess() {
Toast.makeText(this, "登錄成功", Toast.LENGTH_SHORT).show();
}
}
最重要的 Presenter 層
LoginPresenter 類,只用實現(xiàn)兩個方法垦巴,一個是登錄媳搪,一個是銷毀。
public interface LoginPresenter {
void validateUser(UserBean userBean);
void onDestroy();
}
之前提到的 OnLoginFinishListener
public interface OnLoginFinishListener {
void onUserError();
void onPasswordError();
void onSuccess();
}
LoginPresenter 的實現(xiàn)類骤宣,這里在登錄方法里面調(diào)用 Model 的登錄秦爆,并且在登錄的回調(diào)里面調(diào)用 LoginView 的方法,通過這么做來鏈接 Model 層和 View 層憔披,由此可見對于 MVP 模式而言等限,最重要的是 Presenter 層,View 層和 Model 層只要管好自己的事芬膝,Model 層管好業(yè)務(wù)邏輯望门,View 層管好 ui 交互,而對于 presenter 來說最重要的就是處理兩者之間的關(guān)系锰霜,作為一個協(xié)調(diào)者筹误。