浅谈MVP模式

作者 Lin Wait For Li 日期 2017-03-01
浅谈MVP模式

MVP模式三部分

  1. model(处理业务逻辑)
  2. view(更新UI界面)
  3. presenter(连接view与model之间的角色)
    其中三者关系如下图所示:
    mvp_first
    用户仅仅接触到的只是View,而不再是Controller模块。这样当逻辑处理业务过多的时候就不会增加界面类的代码,UI仅仅只做UI更新操作,大家各司其职。

MVP模式实例代码

model类

  1. 首先创建一个model的接口,此接口是为了让不同的model去实现这个接口,能储存不同类型的数据,使得成为数据的出入口。
    代码如下:

    public interface MessageModel {
    void setNumber(Object object);
    Number getNumber();
    }
  2. 实现上述model接口,让具体的model能储存需要用到的数据。
    代码如下:

    public class MyMedol implements MessageModel {
    private Number number;
    @Override
    public void setNumber(Object object) {
    number = (Number)object;
    }
    @Override
    public Number getNumber() {
    return number;
    }
    }

view类

  1. 创建一个view的接口,该接口是监听presenter与view之间活动通信,并且触发presenter对UI更新操作。
    代码如下:
    public interface CommonView {
    void setNewMessage(int id,Object obejct);
    }

presenter类

  1. 创建一个presenter类,成为model类与view类之间的通信桥梁。
    代码如下:
    public class MyPresenter {
    private CommonView commonView;
    private MyMedol mMedol;
    private HashMap<String,Integer> map;
    private Number numberAdd;
    public MyPresenter(CommonView commonView) {
    this.commonView = commonView;
    mMedol = new MyMedol();
    }
    public void saveNumber(Number number){
    mMedol.setNumber(number);
    }
    /**
    * 具体逻辑业务处理
    */
    public void updateUI(){
    numberAdd = mMedol.getNumber();
    int first = Integer.parseInt(numberAdd.getFirstNumber());
    int second = Integer.parseInt(numberAdd.getSecondNumber());
    //加法
    numberAdd.setResult(first + second);
    commonView.setNewMessage(map.get("add"),numberAdd);
    //乘法
    Number numberMul = new Number();
    numberMul.setResult(first * second);
    commonView.setNewMessage(map.get("mul"),numberMul);
    //相减
    Number numberSub = new Number();
    numberSub.setResult(first - second);
    commonView.setNewMessage(map.get("sub"),numberSub);
    }
    /**
    * 假如有很多控件需要更新ID,可以通过设置UI需要更新的ID,再判断具体 * 的id去更新数据
    * @param map
    */
    public void setUIofId(HashMap<String,Integer> map){
    this.map = map;
    }
    }

测试开始

  1. 首先需要实现CommonView的接口,让当前View与Presenter建立连接。
    代码如下:
    public class MvpActivity extends Activity implements CommonView{
    private TextView txt_mul,txt_add,txt_sub;
    private EditText edit_first,edit_second;
    private Button mButton;
    private MyPresenter myPresenter;
    private Number number = new Number();
    private HashMap<String,Integer> idMap = new HashMap<String,Integer>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_mvp);
    txt_mul = (TextView)findViewById(R.id.txt_mul);
    txt_add = (TextView)findViewById(R.id.txt_add);
    txt_sub = (TextView)findViewById(R.id.txt_sub);
    edit_first = (EditText)findViewById(R.id.edit_firstNumber);
    edit_second = (EditText)findViewById(R.id.edit_secondNumber);
    mButton = (Button)findViewById(R.id.btn_start);
    //将具体的presenter与当前CommonView绑定,让其相互关联
    myPresenter = new MyPresenter(this);
    //储存需要显示的UI
    idMap.put("mul",R.id.txt_mul);
    idMap.put("add",R.id.txt_add);
    idMap.put("sub",R.id.txt_sub);
    myPresenter.setUIofId(idMap);//设置需要UI更新控件的id
    mButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    number.setFirstNumber(edit_first.getText().toString());
    number.setSecondNumber(edit_second.getText().toString());
    myPresenter.saveNumber(number);
    myPresenter.updateUI();//点击开始之后就更新UI
    }
    });
    }
    @Override
    public void setNewMessage(int id, Object obejct) {
    Number number = (Number) obejct;
    Log.i("MvpActivity","id="+R.id.txt_add);
    switch (id){
    case R.id.txt_mul:
    txt_mul.setText(number.getResult()+"");
    break;
    case R.id.txt_add:
    txt_add.setText(number.getResult()+"");
    break;
    case R.id.txt_sub:
    txt_sub.setText(number.getResult()+"");
    break;
    }
    }
    }

详谈运行过程

  1. 首先创建的MyPresenter类中就实例化时候就会伴随着生产一个CommonView实例化接口,用于与UI做关系连接
  2. MyPresenter类中有实例化后的MyMedol对象,再为储存数据创建一个类中的方法,使其在调用方法(saveNumber方法)时候完成数据的储存。
  3. 在UI中实现CommonView的接口,然后通过myPresenter = new MyPresenter(this)就可以让presenter能与UI沟通并且执行UI更新。
  4. 假如你UI中涉及需要更新的UI控件很多,你可以像我这样,在presenter中创建一个(setUIofId方法),将所有需要更新的UI控件的id从UI传递到presenter中。再绑定数据与对应控件的id一并返回给UI,在UI(setNewMessage方法)中进行识别。
  5. 具体调用图解
    mvp_true

mvp模式优缺点

优点

  1. UI(activity)无需进行数据业务处理逻辑,代码之间耦合度降低。
  2. 代码扩展方便,只需要在对应的presenter中添加新逻辑处理功能即可。
  3. 减少UI的代码行数,修改UI控件方便。

缺点

  1. 类文件过多,因为每一个UI就要对应一个Presenter,一个Presenter又至少需要对应一个model,导致类膨胀。

测试代码运行截图

首先添加第一个数字,再填入第二个数字,然后在MyPresenter进行加、减、乘法的逻辑运算,最后显示出来结果。
mvp