MVP架構思想

序言

一年前,接觸了自己的第一個MVP的Android項目,當時仔細研究過一段時間這種架構,但是后來一直負責的項目都是基于MVC,現在都要忘記了,現在把他記錄下來。

背景

什么叫MVP,為什么要用MVP?

15年實習的時候,很幸運開始從0開始做第一個項目,老大把框架搭建起來,后來開始寫業務邏輯代碼,當時所在的公司是做電商的,需求那就是3個字,改,改,改,Activity的代碼上千行已經習以為常,這樣的代碼變得非常臃腫,難以維護。

那么什么叫MVP?就是Modle——View——Presenter,看到這個概念很懵逼,先甩一張圖看一下和普通MVC的區別:


image

從圖中可以很明顯的看到MVC和MVP的區別,MVP消除了View和Model之間的相互依賴,中間通過Presenter來通訊,解耦合。

MVC和MVP各個部分在干什么?

MVC和MVP兩個單詞只差了一個字母,但是兩種處理方式上改變得太多。

MVC三個部分分別在干什么

  • View:對應于布局文件,但是細細的想一想這個View對應于布局文件,其實能做的事情特別少,實際上關于布局文件中的數據綁定的操作,事件處理的操作都在Activity中,造成了Activity既像View又像Controller。
  • Model:業務邏輯和實體模型
  • Controller:對應于Activity

MVP三個部分分別在干什么

  • View:對應于Activity,負責View的繪制以及用戶交互
  • Model:業務邏輯和實體邏輯
  • Presenter:負責完成View與Model之間的交互

寫一個MVP的demo

  • 定義Modle
    1.定義bean類:
public class People {
    public int icon;
    public String like;
    public String style;

    public int getIcon() {
        return icon;
    }

    public String getLike() {
        return like;
    }

    public String getStyle() {
        return style;
    }

    public void setIcon(int icon) {
        this.icon = icon;
    }

    public void setLike(String like) {
        this.like = like;
    }

    public void setStyle(String style) {
        this.style = style;
    }

    @Override
    public String toString() {
        return "Pepole{" +
                "icon=" + icon +
                ", like='" + like + '\'' +
                ", style='" + style + '\'' +
                '}';
    }

2.定義Model接口:

public interface IPeopleModel {
    void loadPeople(PeolpleOnLoadListener girlOnLoadListener);
    interface PeolpleOnLoadListener{
        void onComplete(List<People> girls);
    }
}

model處理數據邏輯:

public class PeopleModelImpl implements IPeopleModel {
    Handler handler = new Handler(Looper.getMainLooper());

    @Override
    public void loadPeople(final PeolpleOnLoadListener peopleOnLoadListener) {
        new Thread() {
            @Override
            public void run() {
                try {
                    sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                final List<People> data = new ArrayList<People>();
                data.add(new People(R.drawable.f1, "五顆星", "美女1"));
                data.add(new People(R.drawable.f2, "四顆星", "美女2"));
                data.add(new People(R.drawable.f3, "五顆星", "美女3"));
                data.add(new People(R.drawable.f4, "三顆星", "美女4"));
                data.add(new People(R.drawable.f5, "五顆星", "美女5"));
                data.add(new People(R.drawable.f6, "三顆星", "美女6"));
                data.add(new People(R.drawable.f7, "四顆星", "美女7"));
                data.add(new People(R.drawable.f8, "五顆星", "美女8"));
                data.add(new People(R.drawable.f9, "四顆星", "美女9"));
                data.add(new People(R.drawable.f10, "三顆星", "美女10"));
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        //回調
                        peopleOnLoadListener.onComplete(data);
                    }
                });
            }
        }.start();
    }
}
  • 定義Presenter
    從上面的圖中可以看到,Presenter依賴了Model,也依賴了View,所以應該持有他們的引用。因為項目中會有很多的Presenter需要去編寫,也有很多的代碼會重復,所以把這些共有的東西進行封裝,放到BasePresenter中。

1.編寫基類Presenter

public abstract class BasePresenter<T> {
    protected WeakReference<T> mViewRef;
    public abstract void fetch();
    public void attachView(T view){
        mViewRef = new WeakReference<T>(view);
    }

    public void detach(){
        if(mViewRef!=null){
            mViewRef.clear();
            mViewRef = null;
        }
    }

}

2.編寫具體的Presenter

public class PeoplePresenterImpl extends BasePresenter<IPeopleView> {

    IPeopleView  iGirlView;
    public PeoplePresenterImpl(IPeopleView iGirlView){
        this.iGirlView = iGirlView;
    }
    IPeopleModel peopleModel = new PeopleModelImpl();
    @Override
    public void fetch() {
        iGirlView.showloading();
        if(peopleModel!=null){
            peopleModel.loadPeople(new IPeopleModel.PeolpleOnLoadListener() {
                @Override
                public void onComplete(List<People> peoples) {
                    iPeopleView.showPeople(peoples);
                }
            });
        }
    }
}

  • 定義View。

1.先確定我們的View中干什么,定義功能接口

public interface IPeopleView {

    void  showloading();
    void showPeople(List<People> girls);
}

2.因為View要依賴Presenter,所以我們每個Activity肯定會持有一份Presenter的引用,所以在Activity銷毀的時候要釋放,這些操作放到BaseActivity中。

public abstract class BaseActivity<T extends BasePresenter> extends AppCompatActivity {
    protected  T mPresent;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPresent = createPresent();
        mPresent.attachView(this);
    }

    @Override
    protected void onDestroy() {
        mPresent.detach();
        super.onDestroy();
    }

    public abstract T createPresent() ;
}

3.編寫具體的Activity

public class MainActivity extends BaseActivity<PeoplePresenterImpl> implements IPeopleView {
    private ListView listView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_v1);
        listView = (ListView) findViewById(R.id.listview);
        mPresent.fetch();
    }

    @Override
    public PeoplePresenterImpl createPresent() {
        return new PeoplePresenterImpl(this);
    }

    @Override
    public void showloading() {
        Toast.makeText(this,"正在拼命加載",Toast.LENGTH_SHORT).show();
    }

    @Override
    public void showPeople(List<People> girls) {
        listView.setAdapter(new GirlListAdapter(this,girls));
    }
}

OK,MVP架構的練手項目就已經寫完了。可以看到MVP模式中,Activity的代碼會少很多。只是在展示,數據的處理和業務放在了model和presenter中。

效果圖:

image

歸納(套路)

現在,每個人應該都聽過一個詞,叫套路,沒錯,做任何事情都有套路,把套路搞清楚了,再復雜的東西變得簡單一些,總結一下編寫MVP模式的套路:

  • View:負責繪制UI元素,與用戶進行交互,也就是我們的Activity。他可以從模型中讀取數據,但是不能修改或更新模型。view提供提供UI交互,在presenter的控制下修改UI,將業務事件交由presenter處理,注意: View層不存儲數據,不與Model層交互,在Android中View層一般是Activity、Fragment、View(控件)、ViewGroup(布局等)等
  • Activity interface:需要View實現的接口,View通過View interface與Presenter進行交互,降低耦合,方便進行單元測試
  • Model:負責存儲、檢索、操縱數據(有時也實現一個Model interface用來降低耦合);表示數據模型和業務邏輯(business logic)。模型并不總是DataSet,DataTable之類的東西,它代表著一類組件(components)或類(class),這些組件或類可以向外部提供數據,同時也能從外部獲取數據并將這些數據存儲在某個地方。簡單的理解,可以把模型想象成“外觀類(facade class)。職責就是從網絡,數據庫,文件,傳感器,第三方等數據源讀寫數據, 對外部的數據類型進行解析轉換為APP內部數據交由上層處理,對數據的臨時存儲,管理,協調上層數據請求。
  • Presenter:作為View與Model交互的中間紐帶,處理與用戶交互的負責邏輯。

MVP的好處

  • 減少了Activity的職責,簡化了Activity中的代碼,將復雜的邏輯代碼提取到了Presenter中進行處理。與之對應的好處就是,耦合度更低
  • Activity代碼變得更加整潔,使用MVP之后,Activity就能瘦身許多了,基本上只有FindView、SetListener以及Init的代碼。其他的就是對Presenter的調用,還有對View接口的實現。這種情形下閱讀代碼就容易多了,而且你只要看Presenter的接口,就能明白這個模塊都有哪些業務,很快就能定位到具體代碼。Activity變得容易看懂,容易維護,以后要調整業務、刪減功能也就變得簡單許多。
  • 方便進行單元測試,般單元測試都是用來測試某些新加的業務邏輯有沒有問題,如果采用傳統的代碼風格(習慣性上叫做MV模式,少了P),我們可能要先在Activity里寫一段測試代碼,測試完了再把測試代碼刪掉換成正式代碼,這時如果發現業務有問題又得換回測試代碼,咦,測試代碼已經刪掉了!好吧重新寫吧……

MVP中,由于業務邏輯都在Presenter里,我們完全可以寫一個PresenterTest的實現類繼承Presenter的接口,現在只要在Activity里把Presenter的創建換成PresenterTest,就能進行單元測試了,測試完再換回來即可。萬一發現還得進行測試,那就再換成PresenterTest吧。

MVP的壞處

接手的MVP的大項目中,有時候層層回調讓我不知所云,雖然代碼的耦合降低,但是讓人一看,會有很懵逼的感覺,代碼寫太多了,沒有MVC好上手。不知道大家有沒有這種感覺。哈哈!!

總結

沒有最好的架構,只有最合適的架構,雖然MVP架構很好,但是在小項目中,MVC就已經很好的支持項目的開發,沒必要用MVP。在大項目中可以考慮MVP的架構。搞定,收工。

源碼點擊下載

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容