浅谈Andorid开发中的MVP模式

来源:互联网 发布:ubuntu 14使用教程 编辑:程序博客网 时间:2024/05/16 17:52

导语:最近公众号后台经常收到一些消息,说能不能讲一些开发模式,经过思考后,我决定讲一讲MVP模式。希望对大家能够有所帮助。并写了一个简单的小demo。


MVP出现的背景

看到MVP,大家肯定会想什么是MVP呢?这个我可以肯定的告诉大家MVP(Most Valuable Player)是最有价值球员的意思,这当然是开玩笑了。之所以会出现MVP这种架构模式,是因为我相信大家在开发App时,肯定会发现,Activity的负担非常重,既要初始化控件,又要写一些逻辑操作的展示等等,有时候很多Activity中的代码都充当了Controller和Model的角色,所以你会发现Activity违背单一职责原则,负担过重。所以,就出现了这么一种架构模式,叫MVP,并不是最有价值球员哦。

什么是MVP架构
MVP就是Model-View-Presenter,MVP是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。作为一种新的模式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会直接从Model中读取数据而不是通过 Controller。

在MVC里,View是可以直接访问Model的!从而,View里会包含Model信息,不可避免的还要包括一些业务逻辑。 在MVC模型里,更关注的Model的不变,而同时有多个对Model的不同显示,及View。所以,在MVC模型里,Model不依赖于View,但是View是依赖于Model的。不仅如此,因为有一些业务逻辑在View里实现了,导致要更改View也是比较困难的,至少那些业务逻辑是无法重用的。

用流程图的方式解释就更清楚了:

MVP和MVC的区别,及MVP是如何解决MVC的问题?

MVP架构:

  •     View 对应于Activity,负责View的绘制以及与用户交互
  •     Model 依然是业务逻辑和实体模型
  •     Presenter 负责完成View于Model间的交互



View不直接与Model交互,而是通过与Presenter交互来与Model间接交互。
Presenter与View的交互是通过接口来进行的。
通常View与Presenter是一对一的,但复杂的View可能绑定多个Presenter来处理逻辑。

MVC架构:

  •     View:对应于布局文件
  •     Model:业务逻辑和实体模型
  •     Controllor:对应于Activity


View可以与Model直接交互。
Controller是基于行为的,并且可以被多个View共享。
可以负责决定显示哪个View。


总结解释一下就是说:从MVC到MVP的一个转变,就是减少了Activity的职责,减轻了它的负担,简化了Activity中的代码和一些操作,将逻辑代码提取到了Presenter中进行处理,降低了其耦合度。

进一步的解释:

在MVP里,Presenter完全把Model和View进行了分离,主要的程序逻辑在Presenter里实现。而且,Presenter与具体的View是没有直接关联的,而是通过定义好的接口进行交互,从而使得在变更View时候可以保持Presenter的不变,即重用! 不仅如此,我们还可以编写测试用的View,模拟用户的各种操作,从而实现对Presenter的测试--而不需要使用自动化的测试工具。 我们甚至可以在Model和View都没有完成时候,就可以通过编写Mock Object(即实现了Model和View的接口,但没有具体的内容的)来测试Presenter的逻辑。 在MVP里,应用程序的逻辑主要在Presenter来实现,其中的View是很薄的一层。因此就有人提出了Presenter First的设计模式,就是根据User Story来首先设计和开发Presenter。在这个过程中,View是很简单的,能够把信息显示清楚就可以了。在后面,根据需要再随便更改View,而对Presenter没有任何的影响了。 如果要实现的UI比较复杂,而且相关的显示逻辑还跟Model有关系,就可以在View和Presenter之间放置一个Adapter。由这个 Adapter来访问Model和View,避免两者之间的关联。而同时,因为Adapter实现了View的接口,从而可以保证与Presenter之间接口的不变。这样就可以保证View和Presenter之间接口的简洁,又不失去UI的灵活性。 在MVP模式里,View只应该有简单的Set/Get的方法,用户输入和设置界面显示的内容,除此就不应该有更多的内容,绝不容许直接访问Model--这就是与MVC很大的不同之处。


MVP的优点

    1.降低耦合度,隐藏数据,Activity中代码更简洁
    2.模块职责划分明显
    3.方便测试驱动开发
    4.代码复用度较高
    5.代码灵活性

MVP架构模式实例

这个实例是根据用户id获取用户信息并展示的一个过程,其中获取用户信息用了一个线程进行了模拟获取。希望大家能够看懂,并对大家有所帮助。

我们先看一下MVP目录结构图


1、Model层
首先是一个javabean User实体类

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package net.loonggg.mvpdemo.bean;  
  2.   
  3. public class User {  
  4.     private String name;  
  5.     private String id;  
  6.     private String sex;  
  7.     private String age;  
  8.   
  9.     public String getName() {  
  10.         return name;  
  11.     }  
  12.   
  13.     public void setName(String name) {  
  14.         this.name = name;  
  15.     }  
  16.   
  17.     public String getId() {  
  18.         return id;  
  19.     }  
  20.   
  21.     public void setId(String id) {  
  22.         this.id = id;  
  23.     }  
  24.   
  25.     public String getSex() {  
  26.         return sex;  
  27.     }  
  28.   
  29.     public void setSex(String sex) {  
  30.         this.sex = sex;  
  31.     }  
  32.   
  33.     public String getAge() {  
  34.         return age;  
  35.     }  
  36.   
  37.     public void setAge(String age) {  
  38.         this.age = age;  
  39.     }  
  40.   
  41. }  
Model层抽象接口实现:package net.loonggg.mvpdemo.model;
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package net.loonggg.mvpdemo.model;  
  2.   
  3. import net.loonggg.mvpdemo.bean.User;  
  4.   
  5. public class GetUserInfo implements IGetUser {  
  6.   
  7.     @Override  
  8.     public void getUserInfo(final int id, final OnUserInfoListener listener) {  
  9.         // 模拟子线程耗时操作  
  10.         new Thread() {  
  11.             @Override  
  12.             public void run() {  
  13.                 try {  
  14.                     Thread.sleep(2000);  
  15.                 } catch (InterruptedException e) {  
  16.                     e.printStackTrace();  
  17.                 }  
  18.                 // 模拟信息获取成功  
  19.                 if (id == 1) {  
  20.                     User user = new User();  
  21.                     user.setName("非著名程序员");  
  22.                     user.setAge("26");  
  23.                     user.setSex("男");  
  24.                     user.setId("1");  
  25.                     listener.getUserInfoSuccess(user);  
  26.                 } else {  
  27.                     listener.getUserInfoFailed();  
  28.                 }  
  29.             }  
  30.         }.start();  
  31.     }  
  32.   
  33. }  

Model层抽象接口

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package net.loonggg.mvpdemo.model;  
  2.   
  3. public interface IGetUser {  
  4.     public void getUserInfo(int id, OnUserInfoListener listener);  
  5. }  

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package net.loonggg.mvpdemo.model;  
  2.   
  3. import net.loonggg.mvpdemo.bean.User;  
  4.   
  5. public interface OnUserInfoListener {  
  6.     void getUserInfoSuccess(User user);  
  7.   
  8.     void getUserInfoFailed();  
  9. }  

2、View层
我们都知道Presenter与View交互是通过接口,所以我们需要定义一个IShowUserView的接口,这个接口封装的方法基本上都跟视图展示有关。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package net.loonggg.mvpdemo.view;  
  2.   
  3. import net.loonggg.mvpdemo.bean.User;  
  4.   
  5. public interface IShowUserView {  
  6.     void showLoading();  
  7.   
  8.     void hideLoading();  
  9.   
  10.     void toMainActivity(User user);  
  11.   
  12.     void showFailedError();  
  13. }  

3、Presenter层
Presenter是Model和View之间交互的桥梁,里面有一些业务逻辑的操作。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package net.loonggg.mvpdemo.presenter;  
  2.   
  3. import net.loonggg.mvpdemo.bean.User;  
  4. import net.loonggg.mvpdemo.model.GetUserInfo;  
  5. import net.loonggg.mvpdemo.model.IGetUser;  
  6. import net.loonggg.mvpdemo.model.OnUserInfoListener;  
  7. import net.loonggg.mvpdemo.view.IShowUserView;  
  8. import android.os.Handler;  
  9.   
  10. public class UserInfoPresenter {  
  11.     private IGetUser iGetUser;  
  12.     private IShowUserView iShowUserView;  
  13.     private Handler mHandler = new Handler();  
  14.   
  15.     public UserInfoPresenter(IShowUserView iShowUserView) {  
  16.         this.iShowUserView = iShowUserView;  
  17.         this.iGetUser = new GetUserInfo();  
  18.     }  
  19.   
  20.     public void getUserInfoToShow(int id) {  
  21.         iShowUserView.showLoading();  
  22.         iGetUser.getUserInfo(id, new OnUserInfoListener() {  
  23.   
  24.             @Override  
  25.             public void getUserInfoSuccess(final User user) {  
  26.                 // 需要在UI线程执行  
  27.                 mHandler.post(new Runnable() {  
  28.                     @Override  
  29.                     public void run() {  
  30.                         iShowUserView.toMainActivity(user);  
  31.                         iShowUserView.hideLoading();  
  32.                     }  
  33.                 });  
  34.             }  
  35.   
  36.             @Override  
  37.             public void getUserInfoFailed() {  
  38.                 mHandler.post(new Runnable() {  
  39.                     @Override  
  40.                     public void run() {  
  41.                         iShowUserView.showFailedError();  
  42.                         iShowUserView.hideLoading();  
  43.                     }  
  44.                 });  
  45.             }  
  46.         });  
  47.     }  
  48.   
  49. }  

4、Activity中的调用

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package net.loonggg.mvpdemo;  
  2.   
  3. import net.loonggg.mvpdemo.bean.User;  
  4. import net.loonggg.mvpdemo.presenter.UserInfoPresenter;  
  5. import net.loonggg.mvpdemo.view.IShowUserView;  
  6. import android.app.Activity;  
  7. import android.app.ProgressDialog;  
  8. import android.os.Bundle;  
  9. import android.view.View;  
  10. import android.widget.Button;  
  11. import android.widget.TextView;  
  12. import android.widget.Toast;  
  13.   
  14. public class MainActivity extends Activity implements IShowUserView {  
  15.     private Button btn;  
  16.     private TextView name_tv, age_tv, sex_tv;  
  17.     private ProgressDialog pd = null;  
  18.     private UserInfoPresenter userInfoPresenter;  
  19.   
  20.     @Override  
  21.     protected void onCreate(Bundle savedInstanceState) {  
  22.         super.onCreate(savedInstanceState);  
  23.         setContentView(R.layout.activity_main);  
  24.         userInfoPresenter = new UserInfoPresenter(this);  
  25.         btn = (Button) findViewById(R.id.btn);  
  26.         name_tv = (TextView) findViewById(R.id.name_tv);  
  27.         age_tv = (TextView) findViewById(R.id.age_tv);  
  28.         sex_tv = (TextView) findViewById(R.id.sex_tv);  
  29.         pd = new ProgressDialog(this);  
  30.         pd.setMessage("正在加载……");  
  31.   
  32.         btn.setOnClickListener(new View.OnClickListener() {  
  33.   
  34.             @Override  
  35.             public void onClick(View v) {  
  36.                 userInfoPresenter.getUserInfoToShow(1);  
  37.             }  
  38.         });  
  39.     }  
  40.   
  41.     @Override  
  42.     public void showLoading() {  
  43.         pd.show();  
  44.     }  
  45.   
  46.     @Override  
  47.     public void hideLoading() {  
  48.         pd.cancel();  
  49.     }  
  50.   
  51.     @Override  
  52.     public void toMainActivity(User user) {  
  53.         name_tv.setText(user.getName());  
  54.         age_tv.setText(user.getAge());  
  55.         sex_tv.setText(user.getSex());  
  56.     }  
  57.   
  58.     @Override  
  59.     public void showFailedError() {  
  60.         Toast.makeText(this"获取信息有误", Toast.LENGTH_SHORT).show();  
  61.     }  
  62.   
  63. }  

 结语:看完实例代码,有点感觉了吧?俗话说好记性不如烂笔头,看不如写,试着自己去写一个,领会一下其中的精神,相信你会豁然开朗。当然有人说这么做,是不是又多了一层,感觉又麻烦了,是吗?降低了耦合度,提取了代码,并增加了复用,代码更简洁,其实好处还是很多的。
0 0