淺談Andorid開發(fā)中的MVP模式

導(dǎo)語:最近公眾號后臺經(jīng)常收到一些消息炮姨,說能不能講一些開發(fā)模式捌刮,經(jīng)過思考后,我決定講一講MVP模式舒岸。希望對大家能夠有所幫助绅作。并寫了一個簡單的小demo。

背景


看到MVP蛾派,大家肯定會想什么是MVP呢俄认?這個我可以肯定的告訴大家MVP(Most Valuable Player)是最有價值球員的意思,這當(dāng)然是開玩笑了洪乍。之所以會出現(xiàn)MVP這種架構(gòu)模式眯杏,是因為我相信大家在開發(fā)App時,肯定會發(fā)現(xiàn)壳澳,Activity的負擔(dān)非常重岂贩,既要初始化控件,又要寫一些邏輯操作的展示等等巷波,有時候很多Activity中的代碼都充當(dāng)了Controller和Model的角色萎津,所以你會發(fā)現(xiàn)Activity違背單一職責(zé)原則,負擔(dān)過重褥紫。所以姜性,就出現(xiàn)了這么一種架構(gòu)模式,叫MVP髓考,并不是最有價值球員哦部念。

什么是MVP架構(gòu)


MVP就是Model-View-Presenter,MVP是從經(jīng)典的模式MVC演變而來氨菇,它們的基本思想有相通的地方:Controller/Presenter負責(zé)邏輯的處理儡炼,Model提供數(shù)據(jù),View負責(zé)顯示查蓉。作為一種新的模式乌询,MVP與MVC有著一個重大的區(qū)別:在MVP中View并不直接使用Model,它們之間的通信是通過Presenter (MVC中的Controller)來進行的豌研,所有的交互都發(fā)生在Presenter內(nèi)部妹田,而在MVC中View會直接從Model中讀取數(shù)據(jù)而不是通過 Controller。在MVC里鹃共,View是可以直接訪問Model的鬼佣!從而,View里會包含Model信息霜浴,不可避免的還要包括一些業(yè)務(wù)邏輯晶衷。 在MVC模型里,更關(guān)注的Model的不變,而同時有多個對Model的不同顯示晌纫,及View税迷。所以,在MVC模型里锹漱,Model不依賴于View箭养,但是View是依賴于Model的。不僅如此凌蔬,因為有一些業(yè)務(wù)邏輯在View里實現(xiàn)了露懒,導(dǎo)致要更改View也是比較困難的闯冷,至少那些業(yè)務(wù)邏輯是無法重用的砂心。用流程圖的方式解釋就更清楚了:


MVP和MVC的區(qū)別,及MVP是如何解決MVC的問題蛇耀?


MVP架構(gòu):
View: 對應(yīng)于Activity辩诞,負責(zé)View的繪制以及與用戶交互
Model: 依然是業(yè)務(wù)邏輯和實體模型
Presenter: 負責(zé)完成View于Model間的交互

View不直接與Model交互,而是通過與Presenter交互來與Model間接交互纺涤。
Presenter與View的交互是通過接口來進行的译暂。
通常View與Presenter是一對一的,但復(fù)雜的View可能綁定多個Presenter來處理邏輯撩炊。

MVC架構(gòu):
View:對應(yīng)于布局文件
Model:業(yè)務(wù)邏輯和實體模型
Controllor:對應(yīng)于Activity


View可以與Model直接交互外永。
Controller是基于行為的,并且可以被多個View共享拧咳。
可以負責(zé)決定顯示哪個View伯顶。

總結(jié)解釋一下就是說:
從MVC到MVP的一個轉(zhuǎn)變,就是減少了Activity的職責(zé)骆膝,減輕了它的負擔(dān)祭衩,簡化了Activity中的代碼和一些操作,將邏輯代碼提取到了Presenter中進行處理阅签,降低了其耦合度掐暮。

進一步的解釋:

在MVP里,Presenter完全把Model和View進行了分離政钟,主要的程序邏輯在Presenter里實現(xiàn)路克。而且,Presenter與具體的View是沒有直接關(guān)聯(lián)的养交,而是通過定義好的接口進行交互精算,從而使得在變更View時候可以保持Presenter的不變,即重用层坠! 不僅如此殖妇,我們還可以編寫測試用的View,模擬用戶的各種操作破花,從而實現(xiàn)對Presenter的測試--而不需要使用自動化的測試工具谦趣。 我們甚至可以在Model和View都沒有完成時候疲吸,就可以通過編寫Mock Object(即實現(xiàn)了Model和View的接口,但沒有具體的內(nèi)容的)來測試Presenter的邏輯前鹅。 在MVP里摘悴,應(yīng)用程序的邏輯主要在Presenter來實現(xiàn),其中的View是很薄的一層舰绘。因此就有人提出了Presenter First的設(shè)計模式蹂喻,就是根據(jù)User Story來首先設(shè)計和開發(fā)Presenter。在這個過程中捂寿,View是很簡單的口四,能夠把信息顯示清楚就可以了。在后面秦陋,根據(jù)需要再隨便更改View蔓彩,而對Presenter沒有任何的影響了滤蝠。 如果要實現(xiàn)的UI比較復(fù)雜巾遭,而且相關(guān)的顯示邏輯還跟Model有關(guān)系,就可以在View和Presenter之間放置一個Adapter辅髓。由這個 Adapter來訪問Model和View顺又,避免兩者之間的關(guān)聯(lián)更卒。而同時,因為Adapter實現(xiàn)了View的接口稚照,從而可以保證與Presenter之間接口的不變蹂空。這樣就可以保證View和Presenter之間接口的簡潔,又不失去UI的靈活性锐锣。 在MVP模式里腌闯,View只應(yīng)該有簡單的Set/Get的方法,用戶輸入和設(shè)置界面顯示的內(nèi)容雕憔,除此就不應(yīng)該有更多的內(nèi)容姿骏,絕不容許直接訪問Model--這就是與MVC很大的不同之處。

MVP的優(yōu)點
1.降低耦合度斤彼,隱藏數(shù)據(jù)分瘦,Activity中代碼更簡潔
2.模塊職責(zé)劃分明顯3.方便測試驅(qū)動開發(fā)4.代碼復(fù)用度較高5.代碼靈活性

MVP架構(gòu)模式實例


這個實例是根據(jù)用戶id獲取用戶信息并展示的一個過程,其中獲取用戶信息用了一個線程進行了模擬獲取琉苇。希望大家能夠看懂嘲玫,并對大家有所幫助。**
我們先看一下MVP目錄結(jié)構(gòu)圖


1并扇、Model層

首先是一個javabean User實體類

package net.loonggg.mvpdemo.bean;

public class User {
    private String name;
    private String id;
    private String sex;
    private String age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

}

Model層抽象接口實現(xiàn):

package net.loonggg.mvpdemo.model;

import net.loonggg.mvpdemo.bean.User;

public class GetUserInfo implements IGetUser {

    @Override
    public void getUserInfo(final int id, final OnUserInfoListener listener) {
        // 模擬子線程耗時操作
        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 模擬信息獲取成功
                if (id == 1) {
                    User user = new User();
                    user.setName("非著名程序員");
                    user.setAge("26");
                    user.setSex("男");
                    user.setId("1");
                    listener.getUserInfoSuccess(user);
                } else {
                    listener.getUserInfoFailed();
                }
            }
        }.start();
    }

}

Model層抽象接口

package net.loonggg.mvpdemo.model;

public interface IGetUser {
    public void getUserInfo(int id, OnUserInfoListener listener);
}
2去团、View層

我們都知道Presenter與View交互是通過接口,所以我們需要定義一個IShowUserView的接口,這個接口封裝的方法基本上都跟視圖展示有關(guān)土陪。

package net.loonggg.mvpdemo.view;

import net.loonggg.mvpdemo.bean.User;

public interface IShowUserView {
    void showLoading();

    void hideLoading();

    void toMainActivity(User user);

    void showFailedError();
}
3昼汗、Presenter層

Presenter是Model和View之間交互的橋梁,里面有一些業(yè)務(wù)邏輯的操作鬼雀。

package net.loonggg.mvpdemo.presenter;

import net.loonggg.mvpdemo.bean.User;
import net.loonggg.mvpdemo.model.GetUserInfo;
import net.loonggg.mvpdemo.model.IGetUser;
import net.loonggg.mvpdemo.model.OnUserInfoListener;
import net.loonggg.mvpdemo.view.IShowUserView;
import android.os.Handler;

public class UserInfoPresenter {
    private IGetUser iGetUser;
    private IShowUserView iShowUserView;
    private Handler mHandler = new Handler();

    public UserInfoPresenter(IShowUserView iShowUserView) {
        this.iShowUserView = iShowUserView;
        this.iGetUser = new GetUserInfo();
    }

    public void getUserInfoToShow(int id) {
        iShowUserView.showLoading();
        iGetUser.getUserInfo(id, new OnUserInfoListener() {

            @Override
            public void getUserInfoSuccess(final User user) {
                // 需要在UI線程執(zhí)行
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        iShowUserView.toMainActivity(user);
                        iShowUserView.hideLoading();
                    }
                });
            }

            @Override
            public void getUserInfoFailed() {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        iShowUserView.showFailedError();
                        iShowUserView.hideLoading();
                    }
                });
            }
        });
    }

}
4顷窒、Activity中的調(diào)用
package net.loonggg.mvpdemo;

import net.loonggg.mvpdemo.bean.User;
import net.loonggg.mvpdemo.presenter.UserInfoPresenter;
import net.loonggg.mvpdemo.view.IShowUserView;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity implements IShowUserView {
    private Button btn;
    private TextView name_tv, age_tv, sex_tv;
    private ProgressDialog pd = null;
    private UserInfoPresenter userInfoPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        userInfoPresenter = new UserInfoPresenter(this);
        btn = (Button) findViewById(R.id.btn);
        name_tv = (TextView) findViewById(R.id.name_tv);
        age_tv = (TextView) findViewById(R.id.age_tv);
        sex_tv = (TextView) findViewById(R.id.sex_tv);
        pd = new ProgressDialog(this);
        pd.setMessage("正在加載……");

        btn.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                userInfoPresenter.getUserInfoToShow(1);
            }
        });
    }

    @Override
    public void showLoading() {
        pd.show();
    }

    @Override
    public void hideLoading() {
        pd.cancel();
    }

    @Override
    public void toMainActivity(User user) {
        name_tv.setText(user.getName());
        age_tv.setText(user.getAge());
        sex_tv.setText(user.getSex());
    }

    @Override
    public void showFailedError() {
        Toast.makeText(this, "獲取信息有誤", Toast.LENGTH_SHORT).show();
    }

}

結(jié)語:看完實例代碼,有點感覺了吧源哩?俗話說好記性不如爛筆頭鞋吉,看不如寫,試著自己去寫一個励烦,領(lǐng)會一下其中的精神谓着,相信你會豁然開朗。當(dāng)然有人說這么做崩侠,是不是又多了一層漆魔,感覺又麻煩了,是嗎却音?降低了耦合度,提取了代碼矢炼,并增加了復(fù)用系瓢,代碼更簡潔,其實好處還是很多的句灌。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末夷陋,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子胰锌,更是在濱河造成了極大的恐慌骗绕,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件资昧,死亡現(xiàn)場離奇詭異酬土,居然都是意外死亡,警方通過查閱死者的電腦和手機格带,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門撤缴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人叽唱,你說我怎么就攤上這事屈呕。” “怎么了棺亭?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵虎眨,是天一觀的道長。 經(jīng)常有香客問我,道長嗽桩,這世上最難降的妖魔是什么钟鸵? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮涤躲,結(jié)果婚禮上棺耍,老公的妹妹穿的比我還像新娘。我一直安慰自己种樱,他們只是感情好蒙袍,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著嫩挤,像睡著了一般害幅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上岂昭,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天以现,我揣著相機與錄音,去河邊找鬼约啊。 笑死邑遏,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的恰矩。 我是一名探鬼主播记盒,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼外傅!你這毒婦竟也來了纪吮?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤萎胰,失蹤者是張志新(化名)和其女友劉穎碾盟,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體技竟,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡冰肴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了灵奖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嚼沿。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖瓷患,靈堂內(nèi)的尸體忽然破棺而出骡尽,到底是詐尸還是另有隱情,我是刑警寧澤擅编,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布攀细,位于F島的核電站箫踩,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏谭贪。R本人自食惡果不足惜境钟,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望俭识。 院中可真熱鬧慨削,春花似錦、人聲如沸套媚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽堤瘤。三九已至玫芦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間本辐,已是汗流浹背桥帆。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留慎皱,地道東北人老虫。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像宝冕,于是被迫代替她去往敵國和親张遭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

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