android MVC && MVP && MVVM分析和對比

 

目錄(?)[+]html

 

  面試的時候被問到這個問題,用過,也瞭解過,可是仍是不夠深刻,總結一下。 
  MVC,MVP和MVVM是軟件比較經常使用的三種軟件架構,這三種架構的目的都是分離關注,避免將過多的邏輯所有堆積在一個類中,以Android爲例,在activity中既有UI的相關處理邏輯,又有數據獲取邏輯,從而致使activity邏輯複雜不單一難以維護。爲了一個應用能夠更好的維護和擴展,咱們須要很好的區分相關層級,要否則之後將數據獲取方式從數據庫變爲網絡獲取時,咱們須要去修改整個activity。架構使得view和數據相互獨立,咱們把應用分紅三個不一樣層級,這樣咱們就可以單獨測試相關層級,使用架構可以把大多數邏輯從activity中移除,方便進行單元測試。java

MVC

  Model View Controller模式,MVC將應用分紅三個主要層級:Model,View和Controller,它強制將邏輯進行分離,數據結構和Controller邏輯與UI是解耦的,因此測試相關模塊變的更簡單。 
  這裏寫圖片描述 
  其實android app的界面開發部分已是聽從MVC模式的,mysql

  • 視圖層(View):通常採用XML文件進行界面的描述,使用的時候能夠很是方便的引入,固然,也可使用JavaScript+HTML等的方式做爲View層,他的職責就是負責顯示從Controller上獲取到的數據(可是xml佈局做爲View來講功能很無力,因此一般Activity也會承擔一部分View的工做)。
  • 控制層(Controller):Android的控制層的重任一般落在了衆多的Activity的肩上,他們從模型層獲取數據,將獲取到的數據綁定到view上,而且還須要監聽用戶的輸入等操做。
  • 模型層(Model):對數據庫的操做、對網絡等的操做都應該在Model裏面處理,固然對業務計算,變動等操做也是必須放在的該層的。
MVC模式具體表如今android上的效果以下圖所示: 
這裏寫圖片描述 
  也能夠看看 這個視頻,介紹的不錯: 
   這裏寫圖片描述 
還有另外的,android 中的adapter也是使用的MVC模式,自定義的adapter至關於Controller。

 

例子

  以一個獲取天氣的例子來講,xml佈局可視爲View層;Activity爲Controller層,控制用戶輸入,將Model層獲取到的數據展現到View層;Model層的實體類固然就是用來獲取網絡數據了。 
  Model層 
  WeatherModel.class接口android

public interface WeatherModel { void getWeather(OnLoadWeatherCallback callback); }

  WeatherModelImpl.class類git

public class WeatherModelImpl implements WeatherModel{ private Context mContext; public WeatherModelImpl(Context context){ mContext = context; } @Override public void getWeather(final OnLoadWeatherCallback callback) { NetApi.getInstance().jsonObjectRequest(mContext, "http://www.weather.com.cn/data/sk/101010100.html", new HashMap<String, String>(), new BaseNetApi.OnNetCallback<JSONObject>() { @Override public void onSuccess(JSONObject jsonObject) { try { jsonObject = new JSONObject(jsonObject.getString("weatherinfo")); WeatherInfo info = new WeatherInfo(); info.city = jsonObject.getString("city"); info.temp = Double.parseDouble(jsonObject.getString("temp")); info.WD = jsonObject.getString("WD"); info.WS = jsonObject.getString("WS"); info.time = jsonObject.getString("time"); callback.onLoadSuccess(info); } catch (JSONException e) { L.e(e); } } @Override public void onFail(NetError netError) { callback.onError(netError); } }); } }

  Controller層 
  WeatherActivity.class類github

public class WeatherActivity extends BaseActivity implements OnLoadWeatherCallback{ private TextView tv_name; private TextView tv_temperature; private TextView tv_wind_d; private TextView tv_wind_s; private TextView tv_time; private LoadingDialog ld; private WeatherModel weatherModel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_weather); tv_name = (TextView) findViewById(R.id.tv_name); tv_temperature = (TextView) findViewById(R.id.tv_temperature); tv_wind_d = (TextView) findViewById(R.id.tv_wind_d); tv_wind_s = (TextView) findViewById(R.id.tv_wind_s); tv_time = (TextView) findViewById(R.id.tv_time); weatherModel = new WeatherModelImpl(this); ld = new LoadingDialog(this); ld.setLoadingText("正在獲取天氣..."); ld.show(); weatherModel.getWeather(this); } private void onShowWeather(WeatherInfo weatherInfo){ tv_name.setText(weatherInfo.city); tv_temperature.setText(weatherInfo.temp+""); tv_wind_d.setText(weatherInfo.WD); tv_wind_s.setText(weatherInfo.WS); tv_time.setText(weatherInfo.time); } @Override public void onLoadSuccess(WeatherInfo info) { ld.dismiss(); onShowWeather(info); } @Override public void onError(NetError error) { ld.dismiss(); T.getInstance().showShort(error.errorCode +" "+ error.errorMessage); } }

  代碼https://github.com/zhaozepeng/MV-X。這個例子邏輯很簡單,Controller層的Activity持有Model層WeatherModel的對象,而後經過該對象去獲取數據,獲取到數據以後,經過View層去顯示。可是上面有提到過xml佈局文件做爲View層,其實能作的事情特別少,實際上關於該佈局文件中的數據綁定的操做,事件處理的代碼都在Activity中,形成了Activity既像View又像Controller(在邏輯簡單的狀況下,視圖層和控制層寫在一塊兒貌似能夠減小几個類= =),因此有這麼一句話面試

Most of the modern Android applications just use View-Model architecture,everything is connected with Activity.算法

因此這時候能夠繼續把Activity拆分,Activity只控制view和接受用戶的輸入,另外新建一個Controller類,這個類不能繼承任何Android自帶類,用來將邏輯拆分出來,避免Activity的難以維護,具體能夠看看這個例子http://mrbool.com/android-mvc-creating-a-model-view-controller-framework-for-android/32335sql

MVP

  Model, View and Presenter模式,MVP模式和MVC模式相似,是由MVC演變而來,MVP將Controller變成Presenter,而且改變了通訊方向,這個模式將應用分爲三個主要層級:Model, View and Presenter。 
  這裏寫圖片描述 
能夠看到Presenter與Model,Presenter與View的通訊都是雙向的,View不與Model發生關係,都是經過Presenter來傳遞,因此Presenter的業務量會顯的很是大,三層之間的交互關係爲:

  1. View接受用戶的交互請求
  2. View將請求轉交給Presenter
  3. Presenter操做Model進行數據庫更新
  4. 數據更新以後,Model通知Presenter數據發生變化
  5. Presenter更新View層的顯示
Model和View層之間是沒有交互的,這是和MVC不一樣的一點:
  • Model層
  • 該層一般是用來處理業務邏輯和實體模型。
  • View層
  • 一般是一個Activity或者Fragment或者View,這取決於應用的結構,它會持有一個Presenter層的引用,因此View惟一作的事情就是在有用戶交互等操做時調用Presenter層的方法。
  • Presenter層
  • 該層用來做爲一箇中間層的角色,它接受Model層的數據,而且處理以後傳遞給View層,還須要處理View層的用戶交互等操做。
View和Presenter的一對一關係意味着一個View只能映射到一個Presenter上,而且View只有Presenter的引用,沒有Model的引用,因此Presenter和View是一個雙向的交互。Presenter無論View層的UI佈局,View的UI佈局變動,Presenter層不須要作任何修改。

 

例子

  MVP 的寫法就有不少了,不一樣人對於 MVP 的寫法各有不一樣,在遵循面向對象的設計原則基礎上都是能夠的,這裏我就以 google 大大的官方 demo 和一個外國大神的 MVP demo 爲例來分析一下,用哪一種形式不要糾結,最重要的是本身用起來順手。

google 官方寫法

  先貼出來源碼地址:https://github.com/googlesamples/android-architecture/tree/todo-mvp/todoapp,分析一下: 
  這裏寫圖片描述 
  在該 demo 中,有一個 BaseView 和 BasePresenter ,而且使用泛型來定義,做用是定義該模塊不一樣 View 和 Presenter 的基礎行爲: 
BaseView.class

public interface BaseView<T> { void setPresenter(T presenter); }

BasePresenter.class

public interface BasePresenter { void start(); }

 

以後的每個頁面的 View 和 Presenter 都要繼承自 BaseView 和 BaseAdapter ,在 demo 中,View 和 Presenter 的接口類都定義在一個 TasksContract 類中: 
TasksContract.class

public interface TasksContract { interface View extends BaseView<Presenter> { void setLoadingIndicator(boolean active); void showTasks(List<Task> tasks); void showAddTask(); void showTaskDetailsUi(String taskId); void showTaskMarkedComplete(); void showTaskMarkedActive(); void showCompletedTasksCleared(); void showLoadingTasksError(); void showNoTasks(); void showActiveFilterLabel(); void showCompletedFilterLabel(); void showAllFilterLabel(); void showNoActiveTasks(); void showNoCompletedTasks(); void showSuccessfullySavedMessage(); boolean isActive(); void showFilteringPopUpMenu(); } interface Presenter extends BasePresenter { void result(int requestCode, int resultCode); void loadTasks(boolean forceUpdate); void addNewTask(); void openTaskDetails(@NonNull Task requestedTask); void completeTask(@NonNull Task completedTask); void activateTask(@NonNull Task activeTask); void clearCompletedTasks(); void setFiltering(TasksFilterType requestType); TasksFilterType getFiltering(); } }

以後就是實現這兩個基礎接口了,demo 中使用的是 Fragment 做爲 View 的角色,把邏輯從 Activity 中抽離出來,這個看本身的編程習慣吧,只使用 Activity 仍是使用 Fragment。先看看 Activity 的代碼: 
AppCompatActivity.class

public class TasksActivity extends AppCompatActivity { private static final String CURRENT_FILTERING_KEY = "CURRENT_FILTERING_KEY"; private DrawerLayout mDrawerLayout; private TasksPresenter mTasksPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.tasks_act); // Set up the toolbar. Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); ActionBar ab = getSupportActionBar(); ab.setHomeAsUpIndicator(R.drawable.ic_menu); ab.setDisplayHomeAsUpEnabled(true); // Set up the navigation drawer. mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerLayout.setStatusBarBackground(R.color.colorPrimaryDark); NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view); if (navigationView != null) { setupDrawerContent(navigationView); } TasksFragment tasksFragment = (TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame); if (tasksFragment == null) { // Create the fragment tasksFragment = TasksFragment.newInstance(); ActivityUtils.addFragmentToActivity( getSupportFragmentManager(), tasksFragment, R.id.contentFrame); } // Create the presenter mTasksPresenter = new TasksPresenter( Injection.provideTasksRepository(getApplicationContext()), tasksFragment); // Load previously saved state, if available. if (savedInstanceState != null) { TasksFilterType currentFiltering = (TasksFilterType) savedInstanceState.getSerializable(CURRENT_FILTERING_KEY); mTasksPresenter.setFiltering(currentFiltering); } } ... }

能夠看到代碼中,經過 Fragment 的靜態方法獲取到一個 Fragment ,而且添加到 activity 中,那麼如今有一個疑問了, Presenter 是如何設置到 Fragment 中的呢?咱們接下來看看 Presenter 類: 
TasksPresenter.class

public class TasksPresenter implements TasksContract.Presenter { ... public TasksPresenter(@NonNull TasksRepository tasksRepository, @NonNull TasksContract.View tasksView) { mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null"); mTasksView = checkNotNull(tasksView, "tasksView cannot be null!"); mTasksView.setPresenter(this); } @Override public void start() { loadTasks(false); } ... }

能夠看到是在 Presenter 的構造函數中 set 的,這也就沒問題了,官方的 MVP 框架就介紹完了,你們去看一下源碼就很清楚了。

其餘可參考寫法

  另一個外國人寫的例子,分析一下: 
  這裏寫圖片描述 
LoginActivity繼承自LoginView;LoginPresenterImpl繼承自LoginPresenter;LoginInteractorImpl繼承自LoginInteractor,因此MVP模式三個層次之間是經過接口來進行交互的,看看源碼: 
LoginInteractorImpl.class類

public class LoginInteractorImpl implements LoginInteractor { @Override public void login(final String username, final String password, final OnLoginFinishedListener listener) { // Mock login. I'm creating a handler to delay the answer a couple of seconds new Handler().postDelayed(new Runnable() { @Override public void run() { boolean error = false; if (TextUtils.isEmpty(username)){ listener.onUsernameError(); error = true; } if (TextUtils.isEmpty(password)){ listener.onPasswordError(); error = true; } if (!error){ listener.onSuccess(); } } }, 2000); } }

LoginActivity.class類

public class LoginActivity extends Activity implements LoginView, View.OnClickListener { private ProgressBar progressBar; private EditText username; private EditText password; private LoginPresenter presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); progressBar = (ProgressBar) findViewById(R.id.progress); username = (EditText) findViewById(R.id.username); password = (EditText) findViewById(R.id.password); findViewById(R.id.button).setOnClickListener(this); presenter = new LoginPresenterImpl(this); } @Override protected void onDestroy() { presenter.onDestroy(); super.onDestroy(); } @Override public void showProgress() { progressBar.setVisibility(View.VISIBLE); } @Override public void hideProgress() { progressBar.setVisibility(View.GONE); } @Override public void setUsernameError() { username.setError(getString(R.string.username_error)); } @Override public void setPasswordError() { password.setError(getString(R.string.password_error)); } @Override public void navigateToHome() { startActivity(new Intent(this, MainActivity.class)); finish(); } @Override public void onClick(View v) { presenter.validateCredentials(username.getText().toString(), password.getText().toString()); } }

LoginPresenterImpl.class類

public class LoginPresenterImpl implements LoginPresenter, OnLoginFinishedListener { private LoginView loginView; private LoginInteractor loginInteractor; public LoginPresenterImpl(LoginView loginView) { this.loginView = loginView; this.loginInteractor = new LoginInteractorImpl(); } @Override public void validateCredentials(String username, String password) { if (loginView != null) { loginView.showProgress(); } loginInteractor.login(username, password, this); } @Override public void onDestroy() { loginView = null; } @Override public void onUsernameError() { if (loginView != null) { loginView.setUsernameError(); loginView.hideProgress(); } } @Override public void onPasswordError() { if (loginView != null) { loginView.setPasswordError(); loginView.hideProgress(); } } @Override public void onSuccess() { if (loginView != null) { loginView.navigateToHome(); } } }

  代碼層次很清楚,View層接受用戶的點擊操做,回調Presenter層的相關接口,Presenter層再調用到Model層去執行登陸操做,同時修改View層的Progress顯示狀況,Model層執行完登陸操做以後,回調到Presenter層的對應接口,Presenter再去對View層的佈局進行相應的修改。源碼https://github.com/zhaozepeng/MV-X,這裏也給一下源連接:https://github.com/antoniolg/androidmvp

MVVM

  Model,View and ViewModel模式,MVVM 模式將 Presenter 更名爲 ViewModel,基本上與 MVP 模式徹底一致,ViewModel能夠理解成是View的數據模型和Presenter的合體,MVVM採用雙向綁定(data-binding):View的變更,自動反映在 ViewModel,反之亦然,這種模式其實是框架替應用開發者作了一些工做,開發者只須要較少的代碼就能實現比較複雜的交互。 
  這裏寫圖片描述

  • Model
  • 相似MVP
  • View
  • 相似MVP
  • ViewModel
  • 注意這裏的「Model」指的是View的Model,跟上面那個Model不是一回事。所謂View的Model就是包含View的一些數據屬性和操做的東西。

例子

  這種模式的關鍵技術就是數據綁定(data binding)。 
  在android中已經有了相應的插件框架,好比 RoboBinding這個框架,可是好像侵入性太強,普及程度不高,因此能夠看看全面介紹Android的MVVM框架 - 數據綁定這篇博客,它使用了databinding依賴庫進行處理,講的很好。 
  也能夠看看https://github.com/fabioCollini/mv2m,外國人寫的一個庫,進行了不少其餘的封裝,能夠參考一下: 
這裏寫圖片描述 
裏面的介紹也很是清楚。

MVC VS MVP VS MVVM

  這裏寫圖片描述 
  MVP模式是從MVC模式演變來的,它們的基本思想有相通的地方:Controller/Presenter負責邏輯的處理,Model提供數據,View負責顯示,因此他們之間並無特別大的不一樣,都是用來將View和Model之間鬆耦合。做爲一種新的模式,MVP與MVC有着一個重大的區別:在MVP中View並不直接使用Model,它們之間的通訊是經過Presenter (MVC中的Controller)來進行的,全部的交互都發生在Presenter內部,而在MVC中是容許Model和View進行交互的。還有重要的一點就是Presenter與View之間的交互是經過接口的。MVVM是經過MVP演變而來的,主要用了data binding來實現雙向交互,這就使得視圖和控制層之間的耦合程度進一步下降,關注點分離更爲完全,同時減輕了Activity的壓力。 
關鍵點總結:

  • MVC
    1. Controller是基於行爲,而且可以在view之間共享
    2. Controller負責接收用戶交互等操做,而且決定須要顯示的視圖。
  • MVP
    1. View和Model更加的解耦了,Presenter負責綁定Model到View。
    2. 複雜的View能夠對應多個Persenter。
    3. Presenter保留有View層的事件邏輯,全部的點擊之類的事件都直接委託給Presenter。
    4. Presenter經過接口直接和View層解耦,因此更加方便的進行View的單元測試。
    5. Presenter和其餘兩層都是雙向調用的。
    6. MVP有兩種實現方式:」Passive View」,View基本包含0邏輯, Presenter做爲View和Model的中間人,View和Model相互隔離,View和Model沒有直接的數據綁定,取而代之的是View提供相關的setter方法供Persenter去調用,這麼作的好處是View和Model乾淨的分離開了,因此更好的進行相關測試,缺點是須要提供不少的setter方法;」Supervising Controller」,Persenter處理用戶交互等的操做,View和Model直接經過數據綁定鏈接,這種模式下,Persenter的任務就是將實體直接經過Model層傳遞給View層,這種方法的好處就是代碼量少了,可是缺點就是測試難度增大,而且View的封裝性變低。
  • MVVM
    1. 用戶直接交互的是View。
    2. View和ViewModel是多對一的關係。
    3. View有ViewModel的引用,可是ViewModel沒有任何關於View的信息。
    4. 支持View和ViewModel的雙向數據綁定。

引用

http://antonioleiva.com/mvp-android/ 
https://medium.com/android-news/android-architecture-2f12e1c7d4db#.aaytu7yff 
http://kb.cnblogs.com/page/120678/ 
http://www.ruanyifeng.com/blog/2015/02/mvcmvp_mvvm.html 
http://www.cnblogs.com/devinzhang/archive/2012/01/26/2329869.html 
http://mrbool.com/android-mvc-creating-a-model-view-controller-framework-for-android/32335 
http://www.bogotobogo.com/DesignPatterns/mvc_model_view_controller_pattern.php 
http://www.cnblogs.com/cuihongyu3503319/archive/2009/01/09/1372820.html 
http://droidumm.blogspot.com/2011/11/concept-model-view-present-mvp-pattern.html 
http://blog.csdn.net/lmj623565791/article/details/46596109 
http://stackoverflow.com/questions/2056/what-are-mvp-and-mvc-and-what-is-the-difference 
http://blog.csdn.net/feelang/article/details/46348079 
http://blog.csdn.net/napolunyishi/article/details/22722345 
http://www.zhihu.com/question/30976423

 相關:

Android App的設計架構:MVC,MVP,MVVM與架構經驗談

Google 官方MVP Demo 

Android MVP 詳解(上)

Android MVP 詳解(下)

相關文章
相關標籤/搜索