今天寫寫Android的MVC、MVP、MVVP三個框架的對比,并加深自己對這三個框架的理解。
一 . MVC:Model-View-Controller
MVC全名是:Model(模型) View(視圖) Controller(控制器) 是軟件[架構]中最常見的框架,簡單來說,就是通過Controller的控制去操作Model層的數據,并且返回給View作展示。
1.MVC的工作原理?
1. View接受用戶的交互請求。
2. View將請求轉交給Controller。
3. Controller操作Model進行數據更新。
4. 數據更新之后,Model通知View數據變化。
5. View顯示更新之后的數據。
2.MVC優點?
1)把業務邏輯全部分離到Controller中,模塊化程度高。當業務邏輯變更的時候,不需要變更View和Model,只需要更換Controller就行了。
2)須手動或通過觀察者模式進行多視圖更新。
3.MVC缺點?
1)Controller測試困難。Controller不知道任何View的細節,一個Controller能被多個View使用。
2)View無法組件化,復用性較差。View是強依賴特定的Model的,如果需要把這個View抽出來作為一個另外一個應用程序可復用的組件就困難了,因為不同程序的的Model是不一樣的。
4.使用場景?
適用于較小,功能較少,業務邏輯較少的項目。
二 . MVP:Model-View-Presenter
Presenter將Model的變化返回給View。和MVC不同的是,presenter會反作用于view,不像controller只會被動的接受view的指揮。正常情況下,發現可以抽象view,暴漏屬性和事件,然后presenter引用view的抽象。這樣可以很容易的構造view的mock對象,提高可單元測試性。在這里,presenter的責任變大了,不僅要操作數據,而且要更新view。
在Passive View中,為了減少UI組件的行為,使用controller不僅控制用戶事件的響應,而且將結果更新到view上。可以集中測試controller,減小view出問題的風險。
1.MVP的工作原理?
1. View接受用戶的交互請求
2. View將請求轉交給Presenter
3. Presenter操作Model進行數據庫更新
4. 數據更新之后,Model通知Presenter數據發生變化
5. Presenter更新View的數據
2.MVP優點?
1.便于測試。
Presenter對View是通過接口進行,在對Presenter進行不依賴UI環境的單元測試的時候。可以通過Mock一個 View對象,這個對象只需要實現了View的接口即可,單元測試的時候就可以完整的測試Presenter業務邏 輯的正確性。
2.View可以進行組件化。
在MVP當中,View不依賴Model。這樣就可以讓View從特定的業務場景中脫離出來,可以說View可以做到對業務邏輯完全無知。它只需要提供一系列接口提供給上層操作。
3.View高度復用。
4.解耦。
3.MVP缺點?
1.維護比較困難。
Presenter中除了業務邏輯以外,還有大量的View->Model,Model->View的手動同步邏輯,造成Presenter比較笨重,維護起來會比較困難。
4.使用場景?
視圖界面不是很多的項目中。
5 . 代碼示例
MainActivity
public class MainActivity extends Activity implements IUserInfoShow {
private static final String TAG = "MVP";
private UserInfoPresenter mUserInfoPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mUserInfoPresenter = new UserInfoPresenter(this, new GetUserInfoImpl());
mUserInfoPresenter.getUserInfo(123);
}
@Override
public void beforeLoding() {
Log.i(TAG, "beforeLoding");
}
@Override
public void getUserInfoSucceed(User user) {
Log.i(TAG, "id:"+user.getId()+" realName:"+user.getRealName());
}
@Override
public void getUserInfoFailed(String msg) {
Log.i(TAG, "msg=" + msg);
}
@Override
public void afterLoading() {
Log.i(TAG, "afterLoading");
}
}
User
/**
* @創建 HaiJia
* @時間 2017/2/7 13:51
* @描述 UserBean
*/
public class User {
private int id;//用戶ID
private String realName;//用戶真實姓名
public String getRealName() {
return realName;
}
public void setRealName(String realName) {
this.realName = realName;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
IUserInfoShow
public interface IUserInfoShow {
void beforeLoding();
void getUserInfoSucceed(User user);
void getUserInfoFailed(String msg);
void afterLoading();
}
// MainActivity extends Activity implements IUserInfoShow
UserInfoPresenter
public class UserInfoPresenter {
private IGetUserInfo mIGetUserInfo;
private IUserInfoShow mUserInfoShow;
public UserInfoPresenter(IUserInfoShow mUserInfoShow, IGetUserInfo mIGetUserInfo) {
this.mUserInfoShow = mUserInfoShow;
this.mIGetUserInfo = mIGetUserInfo;
}
public void getUserInfo(int id){
mUserInfoShow.beforeLoding();
mIGetUserInfo.getUserInfo(id, new OnUserInfoListener() {
@Override
public void getUserInfoSuccess(User user) {
mUserInfoShow.getUserInfoSucceed(user);
mUserInfoShow.afterLoading();
}
@Override
public void getUserInfoFailure(String msg) {
mUserInfoShow.getUserInfoFailed(msg);
mUserInfoShow.afterLoading();
}
});
}
}
IGetUserInfo
/**
* @創建 HaiJia
* @時間 2017/2/7 14:00
* @描述 TODO
*/
public interface IGetUserInfo {
void getUserInfo(int id,OnUserInfoListener listener);
}
OnUserInfoListener
/**
* @創建 HaiJia
* @時間 2017/2/7 14:01
* @描述 TODO
*/
public interface OnUserInfoListener {
void getUserInfoSuccess(User user);
void getUserInfoFailure(String msg);
}
GetUserInfoImpl
/**
* @創建 HaiJia
* @時間 2017/2/7 14:03
* @描述 實現類
*/
public class GetUserInfoImpl implements IGetUserInfo {
@Override
public void getUserInfo(final int id, final OnUserInfoListener listener) {
// 模擬數據
new Thread(new Runnable() {
@Override
public void run() {
if (id == 123){
User user = new User();
user.setId(123);
user.setRealName("黃海佳");
listener.getUserInfoSuccess(user);
}else{
String msg = "用戶數據出錯,請回你的火星去";
listener.getUserInfoFailure(msg);
}
}
}).start();
}
}
三 . MVVM:Model-View-ViewModel
MVVM是在原有領域Model的基礎上添加一個ViewModel,這個ViewModel除了正常的屬性意外,還包括一些供View顯示用的屬性。例如在經典的MVP中,view有一個屬性ischeck,需要在presenter中設置view的ischeck值。但是在MVVM中的presenter也會有一個ischeck屬性來同步view的ischeck屬性,可能會用到observer模式同步ischeck的值。在MVVM中,presenter被改名為ViewModel,就演變成了你看到的MVVM。在支持雙向綁定的平臺,MVVM更受歡迎。例如:微軟的WPF和Silverlight。
1.MVVM優點?
1.提高可維護性。
解決了MVP大量的手動View和Model同步的問題,提供雙向綁定機制。提高了代碼的可維護性。
2.簡化測試。
因為同步邏輯是交由Binder做的,View跟著Model同時變更,所以只需要保證Model的正確性,View就正確。大大減少了對View同步更新的測試。
3.ViewModle易于單元測試。
2.使用場景?
適用于界面展示的數據較多的項目。
3.代碼示例
在Module的build.gradle中添加下面代碼,注意的是保證Gradle插件版本不低于1.5.0-alpha1
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
...
dataBinding {
enabled true//添加這個
}
}
activity_main
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="com.haijia.mvvp.bean.User" />
<variable
name="user"
type="User" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{String.valueOf(user.id)}" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{user.realName}" />
</LinearLayout>
</layout>
MainActivity
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setUser(new User(123,"黃海佳"));
}
}
就是這么簡單就搞定了MVVM的框架,但是這里面有很多細節,下一篇文章在詳細介紹一下data binding框架。
參考
一個Android項目搞定所有主流架構-2.MVP+單元測試