MVC與MVP在項目實踐中的總結

嗯 最近比較休閑了,先泡一杯碧螺春定定神,然后開始總結MVC與MVP的項目運用中的優(yōu)缺點,剛開始用MVP這個架構開始項目搭建的時候可謂是小心翼翼呀,也是激動萬分的;使用之后覺得也就那么回事兒,面向?qū)ο蟮木幊倘f變不離其宗,只有掌握了扎實的編程基礎,一切都好說;

首先簡單闡述一下MVC;

1.png

MVC分為:Model(數(shù)據(jù)抽象)、View(視圖)、Controller(控制器)的三層架構。接下來我們分別來一一解析每一層所對應的職責分別是什么。

View層:對應的則是Android中的layout文件夾中的xml文件,在啟動Activity/Fragment的時候,都會加載一個R.layout.xxx的布局文件,使得在視圖中顯示出我們在xml中定義好的視圖。

Controller層:對應的則是Activity/Fragment。當Activity/Fragment加載了layout文件后,我們需要在Activity/Fragment中findViewById(int)去尋找到相對應的view,并對找到的view設置相應的屬性以及監(jiān)聽器。而在設置view的屬性之前,我們很有可能會先到model中請求一次數(shù)據(jù),當數(shù)據(jù)回調(diào)回來后controller就會去更新view了。

Model層:對應的則是一些DataSource以及DataBean的相關對象,這里的DataSource指的是數(shù)據(jù)的來源。一般數(shù)據(jù)的來源有2個主要的地方,一個是sqlite,一個是webservice,而我們習慣于將這兩種數(shù)據(jù)的來源封裝在一個repository中,對于調(diào)用者而言只需要調(diào)用repository中的一個獲取接口來獲取數(shù)據(jù),但是這個數(shù)據(jù)是從內(nèi)存中還是sqlite還是webservice來,我們都不得而知,從保護了調(diào)用實現(xiàn)的邏輯,分解相關的實現(xiàn),達到調(diào)用者的極度簡單與簡潔,且在單元測試中測試接口也是非常方便的。

首先是View:Activity_view.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<Button
    android:id="@+id/btn_hello_mvc"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:text="Hello MVC" />

</FrameLayout>
接下來是Controller:ControllerActivity.java
// Controller
public class ControllerActivity extends Activity {

private Button mBtn;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.Activity_view);

    // 在此處,controller調(diào)用并訪問了view
    mBtn = (Button) findViewById(R.id.btn_hello_mvc);
    mBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // 對于這個 OnClickListener,是屬于view的,它是view的監(jiān)聽器
            // 在這里,view直接訪問了model
            String btnClickData = ModelDataSource.ins().getBtnClickData();
            Toast.makeText(ControllerActivity.this, btnClickData, Toast.LENGTH_SHORT).show();
        }
    });

    // 在此處controller調(diào)用了model
    String btnText = ModelDataSource.ins().getBtnText();

    // 在此處controller設置了view的屬性
    mBtn.setText(btnText);
}

}
最后則是Model:ModelDataSource.java
// Model
public class ModelDataSource {

private static ModelDataSource mInstance = null;

public static ModelDataSource ins() {
    if (mInstance == null) {
        synchronized (ModelDataSource.class) {
            if (mInstance == null) {
                mInstance = new ModelDataSource();
            }
        }
    }
    return mInstance;
}

private ModelDataSource() {
}

public String getBtnText() {
    // 在這里,
    // 我們可以去數(shù)據(jù)庫中查找數(shù)據(jù),
    // 也可以去網(wǎng)絡中獲取數(shù)據(jù)
    return "I am from ModelDataSource";
}

public String getBtnClickData() {
    // 在這里,
    // 我們可以去數(shù)據(jù)庫中查找數(shù)據(jù),
    // 也可以去網(wǎng)絡中獲取數(shù)據(jù)
    return "Hello MVC!";
}

}

model層很多人會理解為是普通的javabean以及我的大學老師也是這么和我說的,但是我并不這么認為,我不認為model只是很簡單的一個數(shù)據(jù)結構定義,更多的它應該包含大量的數(shù)據(jù)處理和運算的邏輯,例如從數(shù)據(jù)庫中采集數(shù)據(jù)的操作或者通過網(wǎng)絡請求或者通過NetStream的方法來獲取到二進制的數(shù)據(jù),接著將這些二進制轉換為我們設定好的javabean也就是我們定義好的抽象數(shù)據(jù)模型,然后該對象進行傳遞以及顯示到視圖ui上。
17年之前項目用MVC這種性質(zhì)的項目結構是比較多的,M層封裝網(wǎng)絡請求的數(shù)據(jù),在C層去調(diào)用數(shù)據(jù)然后呈現(xiàn)出來;
優(yōu)點:Android開發(fā)中默認使用的框架,易于上手,能在不需要考慮太多需求的情況下快速開發(fā)一些小型demo功能app。
缺點:隨著業(yè)務的擴展controller會變的越來越臃腫和復雜,大大增加了開發(fā)人員的維護成本以及交接成本,使得后期工作難以展開,且隨著邏輯的復雜變化以及時間的推移會出現(xiàn)連開發(fā)人員自身都對當前代碼邏輯的復雜造成錯誤的理解。

MVP介紹:


1516175204.png

從上圖中我們可以很清晰的看到MVP與MVC中的區(qū)別:

從Controller變成了Presenter
去除View和Model之間的調(diào)用關系,從而徹底的分離了Model和View之間的關聯(lián)與耦合
在MVP的架構中,有一個非常大的特點就是view和model之間的通信必須是通過presenter的傳遞,也正是因為這種隔離的關系,使得視圖和數(shù)據(jù)之間的關系變得完全分離。

還是老規(guī)矩,我們分別來介紹一下MVP架構中的:Model(數(shù)據(jù)模型)、View(視圖)、Presenter(主持者)他們?nèi)叩穆氊熞约跋嗷ブg的關系到底是如何運作的。

View層:視圖層,它所對應的不只是layout中的xml文件還包括了Activity/Fragment作為視圖的顯示。這樣做是擴大了View層的職責所在,View不僅是設置ui的顯示和屬性并且還包括了生命周期的回調(diào)。

Presenter層:主持者層,它相當于是Controller中的業(yè)務邏輯部分,它主要是負責view和model層之間的通信,及時的響應view層的請求并主動的調(diào)用model層的數(shù)據(jù)獲取,并且將獲取到的數(shù)據(jù)結果返回給view層中。presenter是另外新建立一個class,并且讓view從創(chuàng)建的時候就持有一個presenter的實例,當view發(fā)生某些請求響應或者生命周期發(fā)生變化,則會迅速的向presenter發(fā)起請求,讓presenter做出響應的處理,比如:刷新數(shù)據(jù)、清除數(shù)據(jù)防止泄露等。

Model層:此處的數(shù)據(jù)抽象層model和MVC中的model層是一樣的,這里就不做更多的敘述。

view和presenter兩者之間的通信并不是想怎么調(diào)用就可以怎么調(diào)用的,他們之間有著一個標準的協(xié)議,就是在兩者之間定義通用接口IContract,在這個interfac中定義了view層中要暴露的接口也定義了presenter層中需要暴露給view的接口,其目的是利用接口的方式將兩者進行隔離,兩者之間誰都不認識誰的實現(xiàn),達到面向接口編程的目的:
簡單的代碼邏輯:
// Contract
public interface IContract {
interface View {

    void updateBtnText(String s);

    void showToast(String s);
}

interface Presenter {

    /**
     * 調(diào)用該方法表示presenter被激活了
     */
    void start();

    void loadClickString();

    /**
     * 調(diào)用此方法表示presenter要結束了
     * 其目的是為了接觸相互持有導致的內(nèi)存泄露
     */
    void destroy();
}

}
View層ViewActivity.java:
// View
public class ViewActivity extends Activity implements IContract.View {

private Button mBtn;
private IContract.Presenter mPresenter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.Activity_view);

    // 在最開始的時候構建presenter
    mPresenter = new Presenter(this);

    // View初始化
    mBtn = (Button) findViewById(R.id.btn_hello_mvp);
    mBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mPresenter.loadClickString();
        }
    });
}

@Override
protected void onStart() {
    super.onStart();
    mPresenter.start();
}

@Override
protected void onDestroy() {
    if (mPresenter != null) {
        mPresenter.destroy();
        mPresenter = null;
    }
    super.onDestroy();
}

@Override
public void updateBtnText(String s) {
    mBtn.setText(s);
}

@Override
public void showToast(String s) {
    Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
}

}
Presenter層Presenter.java:
// Presenter
class Presenter implements IContract.Presenter {

private IContract.View mView;

Presenter(IContract.View view) {
    mView = view;
}

@Override
public void start() {
    String s = ModelDataSource.ins().getBtnText();
    mView.updateBtnText(s);
}

@Override
public void loadClickString() {
    String s = ModelDataSource.ins().getBtnClickData();
    mView.showToast(s);
}

@Override
public void destroy() {
    mView = null;
}

}
Model層ModelDataSource.java:
// Model
public class ModelDataSource {

private static ModelDataSource mInstance = null;

public static ModelDataSource ins() {
    if (mInstance == null) {
        synchronized (ModelDataSource.class) {
            if (mInstance == null) {
                mInstance = new ModelDataSource();
            }
        }
    }
    return mInstance;
}

public String getBtnText() {
    // 在這里,
    // 我們可以去數(shù)據(jù)庫中查找數(shù)據(jù),
    // 也可以去網(wǎng)絡中獲取數(shù)據(jù)
    return "I am from ModelDataSource";
}

public String getBtnClickData() {
    // 在這里,
    // 我們可以去數(shù)據(jù)庫中查找數(shù)據(jù),
    // 也可以去網(wǎng)絡中獲取數(shù)據(jù)
    return "Hello MVP!";
}

}

從代碼上看我們可以發(fā)現(xiàn)比起傳統(tǒng)的MVC從代碼數(shù)量上看似乎并沒有減少反而增加了不少的代碼和接口,從邏輯上看似乎有些暈乎。但事實并非如此,當我們理解了MVP后則會發(fā)現(xiàn)這種調(diào)用方式其實是非常清晰的,因為你根本無需去在乎到底是誰在調(diào)用你,你只需要知道:我要讓M做什么并且當M做完后我需要將M得出的結果告訴指定的V即可。同時在邏輯上的理解也是非常容易的。很顯然,從時序圖上我們可以看出其中的調(diào)用關系以及調(diào)用邏輯非常的清晰,并不會出現(xiàn)任何的跨道調(diào)用的現(xiàn)象,程序的執(zhí)行過程是非常有條理性。 因為有Presenter這個角色的存在使得view部分的代碼看上去是非常的清晰的,每一個方法都有它自己的主要傾向和職責所在,彼此之間并不會相互耦合。而Presenter中的代碼也是如此,每一個方法都只處理一件事,并不會做其他無相關的事情。

由此我們可以得出一個結論: 對于view來說:
我需要一個主持者,當出現(xiàn)view事件的響應或者生命周期的變化時,我需要告訴這位主持,我要做些什么。
我會提供一系列通用接口,以便于當主持完成我的請求后,調(diào)用相應的接口讓我明白這件事的結論是如何。
我所有的請求都發(fā)給主持,讓他幫我做決定,但是這件事的決定是如何做,我并不知道,但我需要結果。
對于presenter來說:
我只會接收到請求后找model尋求幫助,等model做完事情后通知我了,我在把結果傳遞給view。
我只知道指揮model做事、讓view顯示數(shù)據(jù),但我不干活。
我相當于一座橋,連接著view和model這兩座島,他們誰也不認識誰,想要通信必須要通過我,如果沒有我,他們兩永遠都不會認識。

優(yōu)點:
使用MVP可達到低耦合高內(nèi)聚并且盡可能的保證了開閉原則,非常符合當前的軟件工程;
由于模塊間的耦合很小,可做并行開發(fā),一邊開發(fā)View,一邊開發(fā)Model;
適合大部分的App,代碼邏輯清晰易懂,大大降低開發(fā)、維護和交接成本;
視圖和底層進行徹底的分離,View發(fā)生改變則只需要修改View部分代碼,底層數(shù)據(jù)實現(xiàn)發(fā)生改變則只需要修改底層Model的代碼。
缺點:對于很小的demo來說構建復雜和麻煩,不適合短期、小型且以后不在做任何維護的模塊開發(fā)。

總結:
雖然MVP是一款非常優(yōu)秀的架構,但是再優(yōu)秀的架構也還是會有缺陷的。在技術的世界中,沒有最完美的架構,只有最符合需求的架構,盡管再優(yōu)秀在完美的架構也還是會有很多不足之處的。在MVP中也是存在不少的不足之處,例如:在構建View和Presenter的時候,我們需要多寫大量的冗余接口,這無非是增加了額外的代碼量。還有就是假設我需要新增方法或者修改某個方法的參數(shù)、返回值等,則至少需要變動3個以上的文件,View,Presenter,以及IContract接口。這些都是MVP的不足之處。但是往往我們不能因為某些可以容忍的不足而放棄,也許放棄可以加快眼前的步伐,但對于未來將深陷到難以自拔了。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,619評論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,155評論 3 425
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,635評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,539評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,255評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,646評論 1 326
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,655評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,838評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,399評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,146評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,338評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,893評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,565評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,983評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,257評論 1 292
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,059評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,296評論 2 376

推薦閱讀更多精彩內(nèi)容

  • 和MVC框架模式一樣,Model模型處理數(shù)據(jù)代碼不變在Android的App開發(fā)中,很多人經(jīng)常會頭疼于App的架構...
    ppice閱讀 4,339評論 2 17
  • Android App的設計架構:MVC,MVP,MVVM與架構經(jīng)驗談1. 架構設計的目的1.1 通過設計使程序模...
    天空在微笑閱讀 4,166評論 1 20
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,698評論 25 708
  • 冬天,不需要風,不需要雨,不需要雪,依舊可以冷的發(fā)顫。我自己確實不喜歡。我家在南方,對于下雪來說,機率渺茫。可是...
    燈德閱讀 163評論 0 0
  • 愿你所有的努力都不會白費 愿你紛擾過后能夢想成真 愿你你去自己喜歡的城市 愿你買下自己喜歡的衣服 愿你過上自己喜歡...
    沐抒閱讀 152評論 0 0