MVP簡單小例子分析

大多數編程語言相關的學習書籍,都會以hello,world這個典型的程序作爲第一個示例。作爲Android應用開發者,無論使用eclipse還是用android studio,在新建項目的時候,一直按IDE默認選擇項,下一步進行下去,就會創建出一個可以運行的hello,world應用程序。對於這個程序,可以認爲是採用MVC模式,對應關係爲:

  • View:對應於佈局文件
  • Model:業務邏輯和實體模型
  • Controller:對應於Activity

    但是數據綁定、事件處理(hello world程序沒有)的代碼都在Activity中,Activity看起來既擔任了View的角色,又擔任了Controller的角色。這樣隨着程序業務邏輯越來越複雜,Activity中的代碼就會越來越多,最終結果就是程序的耦合度越來越高,程序修改和維護越來越難。於是MVP模式的優點就顯示出來了。下面我就以這個最簡單的程序,來談談我對mvp模式的理解。

  先上代碼:

複製代碼

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.joy.etest.MainActivity">

    <TextView
        android:id="@+id/tv_show"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!" />

    <Button
        android:id="@+id/btn_test"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="測試"/>

</LinearLayout>

複製代碼

  佈局文件很簡單,一個 TextView ,程序運行顯示 “Hello World!”。這裏我增加了一個Button,點擊Button,開始倒計時10秒鐘,以此來模擬一些耗時的操作,10秒鐘後,TextView顯示"Hello MVP!"。再看java代碼,在MVC模式下,我們直接在 Activity 中通過 setText 方法,就可以給 TextView 設置顯示的內容。MVP模式相對於MVC模式來說,就是將Controller這部分從Activity中分離出來,讓Activity只擔任View的角色,View和Model之間的橋樑作用由 presenter 來承擔,從而達到解耦的目的。具體的方法就是通過接口回調來實現。下面是時候展現真正的步驟了:

  首先定義一個接口,就叫 IShowView吧,裏面有一個 show 方法,用於給TextView設置顯示內容

package com.example.joy.mvptest;

public interface IShowView {
    void show(String str);
}

  MainActivity實現上述接口:

複製代碼

package com.example.joy.mvptest;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity implements IShowView, View.OnClickListener {

    private TextView mTvShow;
    private Button mBtnTest;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTvShow = (TextView) findViewById(R.id.tv_show);
        mBtnTest = (Button) findViewById(R.id.btn_test);
        mBtnTest.setOnClickListener(this);

    }

    @Override
    public void show(String str) {
        mTvShow.setText(str);
    }

    @Override
    public void onClick(View v) {
        if(v.getId() == R.id.btn_test) {

        }
    }
}

複製代碼

  代碼很簡單。MainActivity實現接口中的show方法,即爲TextView賦值。Button的點擊事件暫時沒有寫。

  其次,presenter模塊也要定義一個接口,與 MainActivity 實現的接口提供類似的方法,就叫 IPresenter 吧:

複製代碼

package com.example.joy.mvptest.presenter;

public interface IPresenter {
    void show();
}

複製代碼

  注意,這個接口裏的 show 方法沒有參數,和前面 IShowView 中的方法簽名不一樣,當然,你也可以不命名爲 show ,方法名可以自己隨便起。這裏的方法參數完全是根據實際需要來確定。有了接口,當然還需要有接口的實現類,命名爲 PresenterComl :

複製代碼

package com.example.joy.mvptest.presenter;

import android.os.AsyncTask;
import android.os.SystemClock;
import android.util.Log;

import com.example.joy.mvptest.IShowView;

public class PresenterComl implements IPresenter {
    private IShowView iShowView;

    public PresenterComl(IShowView iShowView) {
        this.iShowView = iShowView;
    }

    @Override
    public void show() {
        new AsyncTask<Void, Void, Void>() {

            @Override
            protected void onPostExecute(Void aVoid) {
                super.onPostExecute(aVoid);
                iShowView.show("Hello,MVP!");
            }

            @Override
            protected Void doInBackground(Void... params) {
                for (int i = 0; i < 10; i++) {
                    Log.d("doInBackground", "正在下載...預計剩餘時間 " + (10 - i) + "秒");
                    SystemClock.sleep(1000);
                }
                return null;
            }
        }.execute();

    }
}

複製代碼

    此處我用了一個異步任務。將倒計時打印出來,模擬一些耗時的數據操作,比如網絡請求等等。該類中聲明瞭一個IShowView接口的實現類的對象,iShowView,倒計時完成,調用iShowView的show方法,將“結果”傳遞過去,典型的接口回調的用法。

  剩下的最後一步就很清晰了,在 MainActivity 中定義 PresenterComl 類的對象 iPresenter,然後,在Button的點擊事件中,調用 iPresenter 的show方法。注意構造方法中MainActivity本身,作爲實現 IShowView 的類的對象傳遞進去。修改後的 MainActivity 類代碼如下:

複製代碼

package com.example.joy.mvptest;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import com.example.joy.mvptest.presenter.IPresenter;
import com.example.joy.mvptest.presenter.PresenterComl;

public class MainActivity extends AppCompatActivity implements IShowView, View.OnClickListener {

    private TextView mTvShow;
    private Button mBtnTest;

    private IPresenter iPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTvShow = (TextView) findViewById(R.id.tv_show);
        mBtnTest = (Button) findViewById(R.id.btn_test);
        mBtnTest.setOnClickListener(this);

        iPresenter = new PresenterComl(this);
    }

    @Override
    public void show(String str) {
        mTvShow.setText(str);
    }

    @Override
    public void onClick(View v) {
        if(v.getId() == R.id.btn_test) {
            iPresenter.show();
        }
    }
}

複製代碼

    上面的紅色標記的代碼就是這最後一步,將Presenter部分與Activity建立關聯。

  OK,通過這樣一個小程序將MVP模式分析了一下,它的本質其實就是接口回調。當然,對於這樣一個小的不能再小的程序來說,用MVP模式,確實看起來更復雜了。但是代碼邏輯複雜了,MVP模式的優勢就顯示出來了。最後總結一下:

  使用MVP模式會使得代碼多出一些接口,但是使得代碼邏輯更加清晰,尤其是在處理複雜界面和邏輯時,我們可以對同一個activity將每一個業務都抽離成一個Presenter,這樣代碼既清晰、邏輯明確又方便我們擴展。當然如果我們的業務邏輯本身就比較簡單的話使用MVP模式就顯得沒那麼必要。所以我們不需要爲了用它而用它,具體的還是要要業務需要。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章