一個簡單的MVP模式案例

1. 問題背景

首先須要清楚的一點是MVP模式的設計初衷是:爲了解決在MVC模式中,過於複雜的邏輯和界面之間的交互中Activity的職責不單一的問題,Activity既充當了View層,又充當了Controller層的角色。刨除問題的複雜度,直接談MVP模式的優越性,都是耍流氓。java

這也就是爲何咱們不少人,爲何不肯意學習MVP的緣由。可是若是遇到了一個比較複雜的問題,MVP的解耦可以讓你更加輕鬆地應對需求的迭代。android

本文將一個案例來解釋MVP模式的設計方法,可是這裏有一個矛盾點:MVP模式自己應該做用於較複雜問題的,可是本文做爲入門文章又必須使用一個較簡單的場景去設計,這樣才能容易看出MVP的結構。bash

場景描述以下:微信

APP中有一本書(Model),書本的價格會顯示在Activity(View)中,Activity中有兩個按鈕,能夠對書本的價格進行控制(Presenter)。app

2. MVP模式的實現

基於上面提出的場景,咱們先簡單的設計界面:ide

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.orzangleli.mvpdemo.MainActivity">
	
    <TextView
        android:id="@+id/desc"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:textSize="16dp"
        android:gravity="center_vertical"
        android:padding="5dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>
	
    <Button
        android:id="@+id/increase"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="漲價1元"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/decrease"
        app:layout_constraintTop_toBottomOf="@id/desc"
        android:layout_marginTop="60dp"
        />
	
    <Button
        android:id="@+id/decrease"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="降價1元"
        app:layout_constraintLeft_toRightOf="@id/increase"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="@id/increase"
        />
	
</android.support.constraint.ConstraintLayout>
複製代碼

咱們設計書本的數據模型BookVo學習

public class BookVo {
    private String name;
    private int price;
    private String author;

    public String getName() {
        return name;
    }

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

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("BookVo{");
        sb.append("name='").append(name).append('\'');
        sb.append(", price=").append(price);
        sb.append(", author='").append(author).append('\'');
        sb.append('}');
        return sb.toString();
    }
}
複製代碼

Activity只負責顯示書本的信息,不參與漲價/降價的邏輯處理,因此咱們須要建立一個Presenter類,把價格邏輯交給他處理,處理完以後再在Activity中顯示。ui

Presenter類應該具備漲價/降價的能力,所以咱們把Presenter設計成接口更加合理。IPresenter.java的內容以下:this

public interface IPresenter {
    void increasePrice();
    void decreasePrice();
}
複製代碼

再設計一個PresenterImpl類繼承IPresenter接口,並實現漲價和降價的兩個方法。由於須要在Presenter中操做Model,並在View中顯示,因此Presenter須要持有Model和View的對象。Model對象很簡單,直接將BookVo傳給Presenter便可,可是View對象如何處理呢?spa

咱們制定一個IView接口,向Presenter暴露咱們可以提供的能力,好比這個場景裏的顯示書籍信息,因而IView接口內容以下:

public interface IView {
    void showBookInfo(BookVo vo);
}
複製代碼

咱們讓MainActivity實現IView接口,

@Override
public void showBookInfo(BookVo vo) {
    this.mDescTv.setText(vo.toString());
}
複製代碼

如今咱們只須要給Presenter添加一個構造方法就行,構造方法中添加兩個參數:BookVo和IView對象。PresenterImpl.java代碼以下:

public class PresenterImpl implements IPresenter {

    private IView mIView;
    private BookVo mBookVo;

    public PresenterImpl(IView iView, BookVo vo) {
        this.mIView = iView;
        this.mBookVo = vo;
    }

    @Override
    public void increasePrice() {
        Log.i("lxc", " ---> 漲價了一元");
        mBookVo.setPrice(mBookVo.getPrice() + 1);
        this.mIView.showBookInfo(mBookVo);
    }

    @Override
    public void decreasePrice() {
        Log.i("lxc", " ---> 降價了一元");
        mBookVo.setPrice(mBookVo.getPrice() - 1);
        this.mIView.showBookInfo(mBookVo);
    }
}
複製代碼

MainActivity代碼以下:

public class MainActivity extends AppCompatActivity implements IView{

    private TextView mDescTv;
    private Button mIncreaseBtn, mDecreaseBtn;
    private IPresenter mPresenter;

    private BookVo vo;

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

        initData();

        initPresenter();

        mDescTv = findViewById(R.id.desc);
        mIncreaseBtn = findViewById(R.id.increase);
        mDecreaseBtn = findViewById(R.id.decrease);

        mIncreaseBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPresenter.increasePrice();
            }
        });

        mDecreaseBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPresenter.decreasePrice();
            }
        });

        mDescTv.setText(vo.toString());

    }

    private void initData() {
        vo = new BookVo();
        vo.setName("《百年孤獨》");
        vo.setAuthor("泰戈爾");
        vo.setPrice(100);
    }

    private void initPresenter() {
        mPresenter = new PresenterImpl(this, vo);
    }
    
    @Override
    public void showBookInfo(BookVo vo) {
        this.mDescTv.setText(vo.toString());
    }
}
複製代碼

如今就能夠看到效果了。

3.若干思考

  1. MVP與MVC有什麼區別?有本質區別麼?
  2. 案例中P層和M層爲何要設計成接口?

4. 後續

本文中的Demo源碼和思考答案將存在於個人微信公衆號中,獲取源碼(Source)請回復"S2",獲取答案(Answer)請回復"A2"。另外歡迎你們關注個人微信公衆號~麼麼麼

相關文章
相關標籤/搜索