1. 问题背景

首先需要清楚的一点是MVP模式的设计初衷是:为了解决在MVC模式中,过于复杂的逻辑和界面之间的交互中Activity的职责不单一的问题,Activity既充当了View层,又充当了Controller层的角色。刨除问题的复杂度,直接谈MVP模式的优越性,都是耍流氓。

这也就是为什么我们很多人,为什么不愿意学习MVP的原因。但是如果遇到了一个比较复杂的问题,MVP的解耦能够让你更加轻松地应对需求的迭代。

本文将一个案例来解释MVP模式的设计方法,但是这里有一个矛盾点:MVP模式本身应该作用于较复杂问题的,但是本文作为入门文章又必须使用一个较简单的场景去设计,这样才能容易看出MVP的结构。

场景描述如下:

APP中有一本书(Model),书本的价格会显示在Activity(View)中,Activity中有两个按钮,可以对书本的价格进行控制(Presenter)。

2. MVP模式的实现

基于上面提出的场景,我们先简单的设计界面:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayoutxmlns: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"><TextViewandroid: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"/><Buttonandroid: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"/><Buttonandroid: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;}@Overridepublic 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中显示。

Presenter类应该具有涨价/降价的能力,因此我们把Presenter设计成接口更加合理。IPresenter.java的内容如下:

public interface IPresenter {void increasePrice();void decreasePrice();
}
复制代码

再设计一个PresenterImpl类继承IPresenter接口,并实现涨价和降价的两个方法。因为需要在Presenter中操作Model,并在View中显示,所以Presenter需要持有Model和View的对象。Model对象很简单,直接将BookVo传给Presenter即可,但是View对象如何处理呢?

我们制定一个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;}@Overridepublic void increasePrice() {Log.i("lxc", " ---> 涨价了一元");mBookVo.setPrice(mBookVo.getPrice() + 1);this.mIView.showBookInfo(mBookVo);}@Overridepublic 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;@Overrideprotected 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() {@Overridepublic void onClick(View v) {mPresenter.increasePrice();}});mDecreaseBtn.setOnClickListener(new View.OnClickListener() {@Overridepublic 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);}@Overridepublic void showBookInfo(BookVo vo) {this.mDescTv.setText(vo.toString());}
}
复制代码

现在就可以看到效果了。

3.若干思考

  1. MVP与MVC有什么区别?有本质区别么?
  2. 案例中P层和M层为什么要设计成接口?

4. 后续

本文中的Demo源码和思考答案将存在于我的微信公众号中,获取源码(Source)请回复"S2",获取答案(Answer)请回复"A2"。另外欢迎大家关注我的微信公众号~么么么

一个简单的MVP模式案例相关推荐

  1. 超简单的MVP模式案例

    在说mvp之前,先说下mvc: MVC( Model View Controller):一种将逻辑和视图分隔开来的架构设计,起源于web端.其实 Android应用的开发中本身可视为一种MVC架构. ...

  2. 简单易懂 MVP 模式

    Android MVP 模式 [1] 也不是什么新鲜的东西了,我在自己的项目里也普遍地使用了这个设计模式.当项目越来越庞大.复杂,参与的研发人员越来越多的时候,MVP 模式 的优势就充分显示出来了. ...

  3. php 跳转qq群代码_一个简单QQ群聊案例代码解析(PHP实现)

    问题: 使用面向对象编程的方式实现以下业务逻辑: 1. 张三使用账号a,密码b登录了qq 2. 显示出张三最后的登录的时间 3. 张三查看了 1小时内的行政部门群的信息(这个群里有张三,李四,王五,其 ...

  4. 一个简单的生产者消费者案例

    生产者消费者模式 为什么要引入生产者消费者模式 简单来说就是为了解决多线程下程序先后执行问题 遇到的例:实际生产中出现的场景 服务D依赖于服务A.B.C产生的数据,服务D必须等待A.B.C产生结果并持 ...

  5. [go]---从java到go(02)---一个简单的handler模式的实现

    类似于责任链模式吧,不同类实现相同的入参,执行不同的操作,一个执行完再确定要不要执行下一个. 用go实现: 1.定义一个接口 后面所有的handler都要实现这个接口的handler方法 type I ...

  6. C++ 有关string类的基本语法以及一个简单算法 理论加案例的形式

    #include<iostream> using namespace std; #include"string" //string类的头文件 #include" ...

  7. Pygame飞机大战一个简单的双人模式测试

    在之前完成了飞机大战整个项目以后就想提升一下,所以萌生出了做双人模式的想法. 那么在单人模式的基础上重写一个HeroB,我们将一号玩家称为HeroA class HeroB(Plane):" ...

  8. 从一个简单的AT模式事务例子入手

    傅青阳眯了一下眼睛,心想这个手下飘了,不给点颜色看看是不行了:talk is cheap show me the code.从AT模式的源码来开始讲整个流程. 元始背转身,负手而立,白衣飘飘,一副胸有 ...

  9. 一个简单的MVC模式练习

    控制层Action接受从模型层DAO传来的数据,显现在视图层上. package Action;import java.sql.Connection; import java.sql.SQLExcep ...

最新文章

  1. 软件工程导论结对项目
  2. python使用imbalanced-learn的OneSidedSelection方法进行下采样处理数据不平衡问题
  3. javascript,令人着迷了!
  4. 【Android Gradle 插件】ProductFlavor 配置 ( applicationId 配置 | SdkVersion 相关配置 | version 应用版本配置 )
  5. 主程序与子程序不在同一程序模块中_深度解析S7200系列PLC带参数子程序用法
  6. sonar 质量配置 操作(质量规则)
  7. c# combobox集合数据不显示_excel打开数据时显示乱码/问号amp;看起来一样却v不出来怎么办...
  8. 更新widget 导致widget host(home) 挂掉
  9. mosquitto源码分析(二)
  10. Leetcode每日一题:23.merge-k-sorted-lists(合并K个排序链表)
  11. 应聘c语言面试试题,c语言面试最必考的十道试题,求职必看!!!
  12. Hadoop-MapReduce
  13. 酒柜设计也可以很“特色”
  14. C语言(素数)[解法]:编写prime(m)判断m是否为素数,当m为素数返回1,否则返回0;
  15. 云服务器操作系统 版本选择,云服务器选择操作系统版本
  16. 使用Itext结合Jfreechart图表导出带图表的word文档
  17. Flex布局常见父项属性(一)- flex-direction
  18. moment 与 moment.unix 区别 moment用法
  19. 活死细胞染色——Cell Meter 细胞活性检测试剂盒
  20. 自动切换背景的登录页面

热门文章

  1. boost::graph模块使用write_graphviz 输出 BGL adjacency_list 的简单示例
  2. boost::array用法的测试程序
  3. boost::fusion::as_list用法的测试程序
  4. boost::function_types::components用法的测试程序
  5. ITK:在矢量图像上执行注册
  6. VTK:可视化之Hanoi
  7. VTK:模型之Delaunay3D
  8. VTK:图片之FillWindow
  9. C语言判断一个数是否是回文数Palindrome算法(附完整源码)
  10. c++对象拷贝语意学