MVP這種設計模式在Android領域越來越火,經常會聽到某某Android應用採用了MVP+Retrofit+RxJava的架構,甚至很多工程師都說面試過的人喜歡提MVP,卻並不知道它真正的機制。那麼,究竟什麼是MVP模式,它又與MVC模式有什麼異同呢?
MVP與MVC
相信對於大部分開發者而言,MVC是一種非常熟悉的模式,它廣泛的應用於web等諸多常見的軟件中。MVC將整個工程分爲三個部分,Model(模型)、View(視圖)、Controller(控制器)中,對於以往常用的Android項目而言,更多的人們會將佈局文件作爲View,而將Activity(或Fragment,下同)作爲Controller。
然而,在Activity中實際上做了很多的事情,不僅處理了業務邏輯,有時也會對UI進行操作,這樣就使得Activity看起來既像Controller層又像View層,隨着項目的不斷髮展,這些Activity和Fragment就會變得越來越臃腫,難以擴展和維護,因此便出現了MVP模式。
作爲MVC的演化版本,在MVP模式中,將整個工程分爲了Model(模型)、View(視圖)、Presenter(表示器),與MVC的異同見下圖(來自圖片):
在圖中我們能夠看出,MVP與MVC最大的不同就是在於Model與View並不能直接交互,而是隔着一層Presenter,相比於MVC,MVP中的Model會更少的關注業務邏輯,與View的耦合度也進一步降低,這種模式不僅使得代碼邏輯更加清晰,同時也能減少我們部署以及單元測試的時間。
Demo
網上很多例子都是使用的用戶登錄(當然也有很多比較複雜的),所以實現了一個其他的功能,主要用於說明各模塊之間的關係。
demo實現的效果如下圖,主要功能爲輸入一個字符串,點擊按鈕將它變成大寫並且輸出
在這個demo中的java代碼結構如下圖:
- Activity用於展示內容
- biz是業務邏輯層,用於避免在Model中進行邏輯處理
- model是模型層
- presenter是表示器層,用於處理Model和View之間的交互
- view中封裝了一些接口,和activity共同組成了視圖層
(1)Model層
在Model層中,我們定義了一個Model,名爲MyModel
package com.example.steveyg.androiddemo.model;
/**
* Created by steveyg on 2016/12/20.
*/
public class MyModel {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
一般很少有實體只包含一個屬性,不過此處用於舉例就寫了一個簡單的實體類。接着定義了相應的biz
package com.example.steveyg.androiddemo.biz;
/**
* Created by steveyg on 2016/12/20.
*/
public interface IMyBiz {
public void exec(String str, OnExecListener listener);
}
package com.example.steveyg.androiddemo.biz;
import com.example.steveyg.androiddemo.model.MyModel;
/**
* Created by steveyg on 2016/12/20.
*/
public interface OnExecListener {
void execSuccess(MyModel model);
}
package com.example.steveyg.androiddemo.biz;
import com.example.steveyg.androiddemo.model.MyModel;
/**
* Created by steveyg on 2016/12/20.
*/
public class MyBiz implements IMyBiz {
@Override
public void exec(final String str, final OnExecListener listener) {
new Thread() {
@Override
public void run() {
super.run();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
MyModel model = new MyModel();
String result = str.toUpperCase();
model.setStr(result);
listener.execSuccess(model);
}
}.run();
}
}
在實現IMyBiz接口的時候,採用了異步的操作,因爲大部分需要處理數據的時候都是要和數據庫或服務器交互的。
(2)View層
在View層中,首先定義了一個佈局文件,裏面有一個提示語,一個輸入框和一個按鈕:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="com.example.steveyg.androiddemo.activity.MainActivity">
<TextView
android:layout_alignTop="@+id/edit"
android:layout_alignBottom="@+id/edit"
android:gravity="center"
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:text="TEXT : " />
<EditText
android:id="@+id/edit"
android:layout_marginRight="10dp"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/submit"
android:text="submit"
android:layout_marginTop="10dp"
android:layout_below="@+id/edit"
android:layout_centerHorizontal="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
接着,我們定義一個接口IMyView,裏面定義了視圖層需要進行的操作:
package com.example.steveyg.androiddemo.view;
import com.example.steveyg.androiddemo.model.MyModel;
/**
* Created by steveyg on 2016/12/20.
*/
public interface IMyView {
public String getStr();
public void showSuccess(MyModel model);
}
getStr()用來獲取輸入框中的文字,showSuccess()用於展示最後的結果。接着,在Activity中實現相應的方法
package com.example.steveyg.androiddemo.activity;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.example.steveyg.androiddemo.R;
import com.example.steveyg.androiddemo.model.MyModel;
import com.example.steveyg.androiddemo.presenter.MyPresenter;
import com.example.steveyg.androiddemo.view.IMyView;
public class MainActivity extends AppCompatActivity implements IMyView {
private EditText mEditText;
private Button mButton;
private MyPresenter mPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
@Override
public String getStr() {
return mEditText.getText().toString();
}
@Override
public void showSuccess(MyModel model) {
Toast.makeText(this,model.getStr(),Toast.LENGTH_SHORT).show();
}
private void init(){
mPresenter = new MyPresenter(this);
mEditText = (EditText)findViewById(R.id.edit);
mButton = (Button)findViewById(R.id.submit);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPresenter.exec();
}
});
}
}
(3)Presenter層Presenter層主要是讓View和Model進行交互,因此我們定義一個執行相應功能的方法:
package com.example.steveyg.androiddemo.presenter;
import android.os.Handler;
import com.example.steveyg.androiddemo.biz.IMyBiz;
import com.example.steveyg.androiddemo.biz.MyBiz;
import com.example.steveyg.androiddemo.biz.OnExecListener;
import com.example.steveyg.androiddemo.model.MyModel;
import com.example.steveyg.androiddemo.view.IMyView;
/**
* Created by steveyg on 2016/12/20.
*/
public class MyPresenter {
private IMyBiz myBiz;
private IMyView myView;
private Handler mHandler = new Handler();
public MyPresenter( IMyView view) {
this.myBiz = new MyBiz();
this.myView = view;
}
public void exec() {
myBiz.exec(myView.getStr(), new OnExecListener() {
@Override
public void execSuccess(final MyModel model) {
//通過Handler在UI線程中處理
mHandler.post(new Runnable() {
@Override
public void run() {
myView.showSuccess(model);
}
});
}
});
}
}
至此,一個簡單的MVP demo就完成啦,不難看出,相比於MVC模式,代碼量增加了很多,但是邏輯更加清晰,在實際的項目中,還是要好好設計下整個項目的結構,而且選定一種模式最好能夠堅持的使用下去,在中途切換模式,有可能會導致整個項目的重構。
參考資料
http://blog.csdn.net/lmj623565791/article/details/46596109
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0227/2503.html
http://www.cnblogs.com/liuling/archive/2015/12/23/mvp-pattern-android.html