MVC簡介
MVC模式示意圖
web中的MVC
MVC架構在web領域應用很廣泛, 用戶通過界面(view層)操作,然後相應動作會傳遞給Controller,Controller根據業務邏輯去操作數據層(Model層),然後數據層把得到的數據回調給View層進行更新,數據層可能是操作本地數據庫,也可能是訪問服務端獲取數據。 這就是MVC設計模式的基本思想。
從設計上來看,MVC較好的把View視圖層、邏輯處理層(Controller)、數據層(Model)進行了很好的分離,設計上也大致符合設計模式的基本思想:高內聚、低耦合、可複用。
Android中的MVC
但是MVC在Android中的使用卻受到了侷限性,由於Android的view佈局基本都是通過xml佈局文件來完成的,而xml佈局文件是在Activity中加載的, 即使是寫通過代碼手寫view,也通常需要Context上下文,然後像讀取SharedPreference、數據庫等,也都需要Context上下文, 所以很多時候,我們寫代碼,就直接寫在了Activity中。這就導致了Activity的任務過於繁重,當業務多了,Activity就會很臃腫。
舉個簡單的例子,我們需要在界面上顯示附近的人,那麼一進入Activity,在onCreate方法裏就去調用請求服務端得到數據,以便儘快的展示出來,這部分代碼我們可能就直接寫在Activity裏了。
有很多方案可以替代MVP,爲什麼要用MVP?
MVC, MVP模式最本質的區別
實際上,如果是一個有經驗且注重代碼解耦的程序員,他可能會把一些業務封裝到業務類或者工具類中,從而完成對Activity代碼的分層和解耦。這樣的話,其實我們不使用MVP模式也可以達到類似的效果。 但是在現實中,多數程序員還是初級程序員或者只顧完成需求的程序員, 所以這一塊他不一定能做好。 MVP模式的出現就解決了這一問題,我們之所以使用MVP,主要原因也是爲了做到代碼分層和解耦,使代碼更清晰, 而MVP模式又是一種通用的設計模式,別人把設計模式的架子都搭好了,我們只需要照貓畫虎,按照這個模式做就行了。 如果我們統一了編程規範,哪怕初級初級程序員也知道該怎麼做,即便他不知道爲什麼要這麼做。 MVP最重要的就是統一了我們的編程規範!
其實,我們如果掌握了代碼設計的幾個核心:高內聚、低耦合、可複用、易維護、易擴展、易移植等,我們就可以完全脫離設計模式的限定。之所以有MVC、MVP這樣具體的設計模式,一個是這些模式確實設計得好,所以大家都在用,也得到了大家的公認; 另一方面,就是爲了統一編程規範, 如果我們開發前就說好,這個APP,或者重構模塊使用MVP模式,那麼即便是多人開發,由於碼規範是一致的,不管是高級還是初級程序員,那麼代碼的整體分層就是差不多了。
其他設計模式也是類似的,其實都是爲了統一編程規範。比如一說單例模式、工廠模式等,你就知道代碼大體該怎麼寫。我們常說的設計模式有二十幾種,實際上遠遠不止,因爲我們在實際工作中,可能還創造了不同的設計模式,只是有的沒有形成行業統一規範,只在公司內部用等原因,讓大家所不知而已。
所以Android剛開始使用MVC設計模式,一個是使用習慣,另一個是Android誕生後還沒有一個好的統一適用於Android的設計模式,隨着不斷髮展,出現了新的公認的設計模式。
MVP模式
MVP模式示意圖
前面介紹了爲什麼誕生了MVP,現在講這個模式, MVP,分爲了三層:
- 1、Model: 數據相關層;
- 2、View : Activity的佈局等View相關的內容;
- 3、Presenter:紐帶層,連接Model和View
既然前面都說了,具體的某種開發模式就是一種編程的規範,類似於單例模式都是有大體套路的,那麼MVP模式自然也就有固定的套路,MVP的開發基本流程如下:
- 1.View層定義View.interface,用來定義View的行爲。一般由Activity或者是Fragment來實現這個接口,它定義了View視圖的各種變化,如設置Textview,加載對話框,更新進度條等。
- 2.Model層定義Modle.interface,這個是用來定義數據層發生變化時的通知接口,因爲Model不能直接與View交互,所以它與Presenter交互,然後再通過Presenter間接達到與View的交互。
- 3.Presenter翻譯的意思是主持人,也就是主持場合,控制節奏的意思。在這時Presenter就負責具體的業務邏輯,請求數據,把數據送到Model,或者監聽Model的數據變化,接受View層的動作,負責通過通知View層的視圖變化。
MVC和MVP的相同點
都是分爲了三層,且model層和view層的定義是相同的
MVC和MVP的不同點
MVP中Presenter取代了MVC中的Controller, 且MVC中Model、View、Controller之間相互發生通信,而MVP中Model與Presenter相互通信,View與Presenter相互通信,而Model與View之間沒有通信。
使用MVP模式的優缺點
優點: 使代碼分層更清晰,單個類的作用更單一,也更方便維護,類的作用就是要求儘量功能單一。
缺點:類會變得較多,且相同功能代碼量會上升。
所以沒有完美的方案,只有最合適的方案,根據需求選擇合適的設計模式。
代碼實踐
主要架構如上,分成了三個模塊,
1、Model 層定義了操作數據的行爲,這裏再接口IWeatherModel中進行定義,具體由WeatherModelImpl實現, 實際開發中還可由更多數據類來實現這一個統一接口;
2、 View就是視圖層, 在IWeatherView這個接口中定義View的常用操作,然後由具體的MainActivity來實現這個接口;
3、Presenter層就是紐帶層, 用來連接Model和view層,自然也就會都有Model和View層的引用,通過引用對象來進行回調, 下面看具體代碼:
model層的代碼:
public interface IWeatherModel {
//提供數據
public String getInfo();
//存儲數據
public void setInfo(String info);
}
,
public class WeatherModelImpl implements IWeatherModel {
private String mInfo;
@Override
public String getInfo() {
return mInfo;//具體的操作數據
}
@Override
public void setInfo(String info) {
mInfo = info;//具體的操作數據
}
}
View層的代碼:
public interface IWeatherView {
void onInfoUpdate(String info);//請求、更新數據的回調
void showWaitingDialog();//家在數據的dialog
void hideWaitingDialog();//取消dialog的顯示
}
,
public class MainActivity extends AppCompatActivity implements IWeatherView{
private WeatherPresenter mPresenter;
TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPresenter = new WeatherPresenter(this);
mTextView = findViewById(R.id.tv_info);
}
@Override
public void onInfoUpdate(String info) {
mTextView.setText(info);//更新顯示信息
}
@Override
public void showWaitingDialog() {
//顯示加載框
}
@Override
public void hideWaitingDialog() {
//隱藏加載框
}
}
Presenter層的代碼:
public class WeatherPresenter {
private IWeatherModel mModel; //model 請求數據,保存數據
private IWeatherView mWeatherView;//view 顯示數據,更新數據
public WeatherPresenter(IWeatherView weatherView) {
mWeatherView = weatherView;
mModel = new WeatherModelImpl();
}
//-------------------回調 view 方法 start ----------------------------
private void onInfoUpdate(String info) {
if(mWeatherView != null) {
mWeatherView.onInfoUpdate(info);
}
}
private void showWaitingDialog() {
if(mWeatherView != null) {
mWeatherView.showWaitingDialog();
}
}
private void hideWaitingDialog() {
if(mWeatherView != null) {
mWeatherView.hideWaitingDialog();
}
}
//-------------------回調 view 方法 end ----------------------------
private void saveInfo(String info) {
if(mModel != null) {
mModel.setInfo(info);
}
}
private String getInfo() {
return mModel.getInfo();
}
/**
* 提供給view層,開始請求數據
*/
public void requestWetherInfo(){
getNetworkInfo();;
}
//網絡請求得到數據後,保存到model層
private void getNetworkInfo(){
new Thread(new Runnable() {
@Override
public void run() {
try {
//打開等待對話框
showWaitingDialog();
//模擬網絡耗時
Thread.sleep(6000);
String info = "21度,晴轉多雲";
//保存到Model層
saveInfo(info);
//從Model層獲取數據,爲了演示效果,實際開發中根據情況需要。
String localinfo = getInfo();
//通知View層改變視圖
onInfoUpdate(localinfo);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//取消對話框
hideWaitingDialog();
}
}
}).start();
}
}
MVP升級版
MVP模式中,Activity可能依然臃腫
MVP中把Layout佈局和Activity作爲View層,增加了Presenter,Presenter層與Model層進行業務的交互,完成後再與View層交互(也就是Activity)進行回調來刷新UI。這樣一來,所有業務邏輯的工作都交給了Presenter中進行,使得View層與Model層的耦合度降低,Activity中的工作也進行了簡化。但是在實際項目中,隨着邏輯的複雜度越來越大,Activity臃腫的缺點仍然體現出來了,因爲Activity中還是充滿了大量與View層無關的代碼,比如各種事件的處理派發,就如MVC中的那樣View層和Controller代碼耦合在一起無法自拔。
針對MVP的優化,爲了把View再次簡化,想到兩種方式.
第一種修改
通過使用一個Presenter代理的方式,在PresenterProxy中處理各種事件機制,View中維護一個PresenterProxy對象當然Presenter中同樣實現了真實對象Presnter所實現的接口,這樣,我們同樣在View中通過代理對象調用真實對象的代碼,結構圖如下:
第二種修改
爲MVP增加一層專門用於處理各種的事件派發Controller層,Controller的作用僅僅是處理事件並根據事件通過維護的Presenter對象派發到對應的業務中,也就是說View層只有一個Controller的對象,View層不會主動去調用Presenter層,但是Controller層和Presenter都可能會回調到View層來刷新UI,所以層次結構就變成了如下:
總結
針對以上修改,可以自行實踐,這種修改可能在大型APP上就會用的到,因爲涉及到的業務會多, 爲了保持代碼的分層和解耦,肯定會增加新的層級, 也就是MVP的升級版。
還是前面講的話,只要我們設計代碼時想要遵循:高內聚、低耦合、可複用、易維護、易修改、易擴展,就得把代碼不斷拆分的更加有層級、單個類功能更加單一,也就會不斷用到新的模式,或者在現有設計模式上不斷進行優化、重組使用。
參考資料:
https://cloud.tencent.com/developer/article/1383090
https://www.cnblogs.com/tiantianbyconan/p/5036289.html