MVP和MVC的区别

2023-11-18

前提回顾

MVC架构:

  MVC就是Model-View-Controller,它们的作用是:

  它们之间的关系如下图所示:

    View传送指令到Controller,Controller完成业务逻辑后,改变Model的状态,Model将新的数据发送到View,这就是MVC模式的处理逻辑。

MVP架构:

MVP是Model-View-Presenter,它们的作用如下:

MVP与MVC的区别

1.Activity职责不同,Activity在MVP中是View层,在MVC中是Controller层,这是MVC和MVP很主要的一个区别,可以说Android从MVC转向MVP开发也主要是优化Activity的代码,避免Activity的代码臃肿庞大。

2.View层不同,MVC的View层指的是XML布局文件或者是用Java自定义的View,MVP的View层是Activity或者Fragment。使用传统的MVC,其中的View,对应的是各种Layout布局文件,但是这些布局文件中并不像Web端那样强大,能做的事情非常有限。MVP的View层Activity在实际项目中,随着逻辑的复杂度越来越大,Activity臃肿的缺点仍然体现出来了,因为Activity中还是充满了大量与View层无关的代码,比如各种事件的处理派发,就如MVC中的那样View层和Controller代码耦合在一起无法自拔。

3.控制层不同,MVC的控制层是Activity,或者是Fragment,Controller对应的是Activity,而Activity中却又具有操作UI的功能,我们在实际的项目中也会有很多UI操作在这一层,也做了很多View中应该做的事情,当然Controller层Activity中也包含Controller应该做的事情,比如各种事件的派发回调,而且在一层中我们会根据事件再去调用Model层操作数据,所以这种MVC的方式在实际项目中,Activity所在的Controller是非常重的,各层次之间的耦合情况也比较严重,不方便单元测试。MVP的控制层是Presenter,里面没有很多的实际东西,主要是做Model和View层的交互。

4.关系链不同,MVP中Model层与View是没有关系的,彼此不会通讯和操作,Model与View的通讯都是Presenter层来传达的。但是在MVC中,Model层和View是曾在交互的。比如我们自定义的View控件里面肯定是要使用Model的数据的,View也要根据不同的Model数据做出不同的展现!这点尤其是体现在自定义的View中,自定义View需要设置数据,用户操作了自定义控件需要改变数据,View要操作Model怎么办?有人说把Controller传到自定义的View啊,现实是不可能没一个自定义View都去持有Controller的引用,其实在MVP中就不会这么尴尬,接口就可以完成。

5.适用范围不同,在Android中,MVP和MVC都用自己的适用情况,使用MVP可以更好的解耦三大模块,模块之间比较清晰,也很方便使用MVP来组件化架构整体项目。但是MVC也是有用武之地的,在组件化的Module或者中间件我们可以使用MVC来做,Module或者中间件不会存在很复杂的View层,使用MVC可以更加方便我们实现功能。

6.交互方式不同,MVP中通讯交互基本都是通过接口的,MVC中的通讯交互很多时候都是实打实的调用对象的方法,简单粗暴!

7.实现方法不同 ,MVC和MVP的Model几乎一样的,都是处理数据,只要不在Activity或者Fragment中请求数据,其他的所有控制都放在Activity或者Fragment中,这样写就基本是MVC的模式,这样写不麻烦,但是很容易把Activity写出上万行代码。用MVP的时候我们需要写很多View和Presenter接口来实现模块之间的通讯,会增加很多类。


相同点: 
优点: 
1.降低耦合度 
2.模块职责划分明显 
3.利于测试驱动开发 
4.代码复用 
5.隐藏数据 
6.代码灵活性 
缺点: 
额外的代码复杂度及学习成本。

不同点: 
MVP模式: 
1.View不直接与Model交互,而是通过与Presenter交互来与Model间接交互 
2.Presenter与View的交互是通过接口来进行的,更有利于添加单元测试 
3.通常View与Presenter是一对一的,但复杂的View可能绑定多个Presenter来处理逻辑,业务相似的时候也可以多同个View共享一个Presenter。 
MVC模式: 
1.View可以与Model直接交互 
2.Controller是基于行为的,并且可以被多个View共享 
3.Controller可以负责决定显示哪个View

不管Activity在MVP中是View层,还是Activity在MVC中是Controller层,都无法避免Activity的代码量越来越大。我们可以根据项目的实际情况尽量优化,MVP和MVC只是一种编码思想,再说再牛逼的架构都抵不过产品的傻逼需求!

作为一种新的模式,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也是比较困难的,至少那些业务逻辑是无法重用的。
虽然 MVC 中的 View 的确“可以”访问 Model,但是我们不建议在 View 中依赖 Model,而是要求尽可能把所有业务逻辑都放在 Controller 中处理,而 View 只和 Controller 交互。

 

mvp相对于mvc的优势

简单的说:我们平时写的Demo都是MVC,controller就是我们的activity,model(数据提供者)就是读取数据库,网络请求这些我们一般有专门的类处理,View一般用自定义控件。但这一切,只是看起来很美。想象实际开发中,我们的activity代码其实是越来越多,model和controller根本没有分离,控件也需要关系数据和业务。

所以说,MVC的真实存在是MC(V),Model和Controller根本没办法分开,并且数据和View严重耦合。这就是它的问题。

举个简单例子 :获取天气数据展示在界面上

 

简单分析下这个例子:

1、activity里面的控件必须关心业务和数据,才能知道自己怎么展示。换句话说,我们很难让两个人在不互相沟通的情况下,一人负责获取数据,一人负责展示UI,然后完成这个功能。
2、所以的逻辑都在activity里面。
完美的体现了MVC的两大缺点,下面看看MVP怎么解决第一个缺点的。

看上图可以看出,从MVC中View被拆成了Presenter和View,真正实现了逻辑处理和View的分离。下面写一个实例:模拟一个登录界面,输入用户名和密码,可以登录以及清除密码。

  • Model层
  • View
    上面说到View层是以接口的形式定义,我们不关心数据,不关心逻辑处理!只关心和用户的交互,那么这个登录界面应该有的操作就是(把这个界面想成一个容器,有输入和输出)获取用户名,获取密码,现实进度条,隐藏进度条,跳转到其他界面,展示失败dialog,清除用户名,清除密码。接下来定义接口:

然后Activity实现这个这个接口:

  • Presenter
    Presenter的作用就是从View层获取用户的输入,传递到Model层进行处理,然后回调给View层,输出给用户!

分析下这个例子:
1、我们有了IUserLoginView 这个接口(协议),activity里面的控件根本不需要关心数据,只要实现这个接口在每个方法中“按部就班”的展示UI就行了。换句话说,我们让两个人一起开发这个功能,一人要处理数据并且制定接口(协议),另一人直接用activity实现这个接口,闭着眼睛就可以在每个回调里展示UI,合作很愉快。
2、MVP成功解决了MVC的第一个缺点,但是逻辑处理还是杂糅在Activity。

MVC到MVP简单说,就是增加了一个接口降低一层耦合。那么,用样的MVP到MVVM就是再加一个接口呗。实际项目我建议用MVP模式,MVVM还是复杂了对于中小型项目有点过度设计,这里就不展开讲。


模块化

上图是一个项目常见的架构方式
1、最底层是基础库,放置与业务无关的模块:比如基础网络请求,图片压缩等等,可以按需分为逻辑模块,通用UI模块和第三方库。(建议采用独立的svn分支)
2、中间层是通用业务层,放置公司多个android项目的通用业务模块(和业务相关的),比如登录流程,文件上传/下载等。
3、最上层就是应用层了,比如公司有三个android项目:LbBoss,BV和BVHD。我们还可以针对相似的项目再抽取通用层(比如这里的BV和BV PAD版,通用层为BVCommon)。

新建一个app,我们往往有两种模块划分方法:

  • 按照类型划分:

  • 按照业务划分:
    每一个包都是一个业务模块,每个模块下再按照类型来分。
  • 怎么选
    我建议中小型的新项目按照类型比较好,因为开始代码量不多按照业务来分不切实际,一个包只放几个文件?? 况且前期业务不稳定,等到开发中期业务定型了,再进行重构难度也不大。

上面讲的模块划分既不属于模块化也不属于插件化,仅仅是一个简单package结构不同而已,app还是一个app并没有产生什么变化。通常讲的模块化,是指把业务划分为不同的moduler(类型是library),每个moduler之间都不依赖,app(类型是application)只是一个空壳依赖所有的moduler。

每个红色箭头都是一个业务模块,红色框是我们的app里面只包含简单的业务:自定义Application,入口Activity,build.gradle编译打包配置。看下项目的依赖关系:

这样架构后,带来最大的不同就是:不同业务模块完全分离,好处就是不同模块的开发绝对不会互相耦合了,因为你在模块A 根本访问不到模块B的API。此时模块间通信急需解决,Intent隐式跳转可以处理部分Activity的跳转,但真正的业务场景远不止两个界面跳一跳。你之前封装的业务通用方法,工具类,数据缓存现在其他模块都拿不到了,本本来可以复用的控件,fragment都不能共享,而这些都是和业务耦合没办法拿到底层基础库。

模块间通信

针对上面问题有两个解决办法,根据自己项目实际情况,如果项目的前期搭建已经很优秀,有完善的基础库,不同模块间的通信不是很多,可以自己实现。如果项目比较庞大,不同业务间频繁调用建议使用阿里巴巴的开源库。

  • 自己实现
    1、首先每个moduler有个目录叫include,里面有三个类,此处以一个bbs论坛模块为例说明,
    IBBSNotify:里面是一堆interface,作用是该模块对外的回调,只能被动被触发。
    IBBService:里面是一堆interface,作用是对外暴露的方法,让别的模块来主动调,比如enterBbsActivity
    IBBSServiceImpl:很明显是IBBService的实现,比如enterBbsActivity就是具体怎么跳转到论坛界面,传递什么数据。

2、每个模块方法和回调都有了,in和out都具备了,别的模块怎么使用呢?就该app该上场了,app不能只是一个壳里面要定义一个ModulerManager implements 所有模块的对外interface,作为每个模块的中转站,A模块告诉ModulerManager我想跳转到论坛模块,接着ModulerManager调用IBBService.enterBbsActivity,IBBSServiceImpl是IBBService的具体实现(多态)然后调用IBBSServiceImpl.enterBbsActivity跳转到BBS界面。
3、通信是解决了,其实踩坑才刚刚开始:
a. 这里的app是我们新建的,那么之前项目的app模块要降为library:

性质发生巨大变化。里面的自定义application,build.gradle,代码混淆配置等全部移到app
b.R.java在Lib类型的moduler中不是final的,所有switch case语句全部替换成if else
c.一定要再建一个common模块,放置通用数据,缓存等
d.还有很多通用功能,例如分享,推送,尽量剥离业务放到common
e.其他与项目相关的细节

  • 开源库ARouter
    专门用于接续模块间通信,这里不讲了使用起来很简单。

其他

插件化:
插件化其实最后发布的产品也是一个apk,只不过大小可以控制(可以随意去掉某些模块),支持用户动态加载子apk。因此,插件化就是动态加载apk。有人说我用intent隐式可以直接跳转到另一个apk啊,干嘛还要插件化。

其实是两码事,intent只是指定一个Activity跳过去,后面的交互完成不受你控制,2个apk也是运行在独立的进程数据无法共享。而插件化可以让两个apk运行在一个进程,可以完全像同一个apk一样开发。不过,我觉得插件化只适合需要多部门并行开发的那种,比如支付宝这种超级app,一般的app开发除非特殊需要,否则用不到。

插件化也有成熟的框架,在此不详细说了。另外,每个人的习惯不一样,组件化,模块化在我看来差不多,没必要纠结两个名词。

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MVP和MVC的区别 的相关文章

  • 华为OD机试 - 字符串排序(C++ & Java & JS & Python)

    目录 描述 输入描述 输出描述 示例1 Java python C 描述 编写一个程序 将输入字符串中的字符按如下规则排序 规则 1 英文字母从 A 到 Z 排列 不区分大小写 如 输入 Type 输出
  • jdbc(2)——之Class.forName(com.mysql.cj.jdbc.Driver)理解

    理解Class forName Com mysql cj jdbc Driver Class forName com mysql cj jdbc Driver 在使用jdbc的时候这行代码必不可少 我们来仔细理解一下这行代码的意义和原理 1
  • 毕业设计 免费送源码36412-SSM 环卫人员管理平台,【计算机毕业设计开题选题+程序定制+论文书写+答辩ppt书写-原创(题目+编号)的定制程序】

    本科生毕业论文 设计 题 目SSM环卫人员管理平台 学 院 XXXXX 专业班级 XXXXX 用户姓名 XXXX 指导人才招聘 XXXX 撰写日期 2023年3月 目 录 摘要 1 绪论 1 1背景及意义 1 2国内外研究概况 1 3研究内
  • 列举几个:MAC OS科研软件推荐

    工欲善其事 必先利其器 部分科研狗也会深爱MAC OS虽然他再科研软件上跟WINDOWS相比还有一定差距 但是MAC OS的人性化轻量也是深得人心 这次就为大家推荐MAC OS上的科研软件 1 科学绘图软件 QtiPlot SciDAVis
  • CS5216

    CS5216特点 DP 到HDMI的显示协议转换器 支持Level shifter Repeater两种应用 应用Dongle或Cable市场 将显卡DP 输出 转接为HDMI输出 CS5216参数 符合高达3 0Gbps的HDMI 1 4
  • Linux之prink原理

    我的分析是基于Linux4 15 1 1 看看kernel是如何调用到console初始化函数的 分两条线 a start kernel gt console init gt call con initcall start 去调用放在 co
  • Java 集合、HashMap 底层实现原理

    一 Java 集合概述 Java 集合可分为 Collection 和 Map 两种体系 Collection接口 单列数据 定义了存取一组对象的方法的集合 List 元素有序 指的是存储时 与存放顺序保持一致 可重复的集合 Set 元素无
  • Mybatis---增删改查

    目录 一 添加用户 1 持久层接口方法 2 映射文件 3 测试方法 二 修改用户 1 持久层接口方法 2 映射文件 3 测试方法 三 删除用户 1 持久层接口方法 2 映射文件 3 测试方法 四 查询用户 1 持久层接口方法 2 映射文件
  • Redis查询当前库有多少个 key

    info可以看到所有库的key数量 dbsize则是当前库key的数量 keys 这种数据量小还可以 大的时候可以直接搞死生产环境 dbsize和keys 统计的key数可能是不一样的 如果没记错的话 keys 统计的是当前db有效的key
  • C++面试题之继承

    目录 设计一个类型 不能在外部环境中创建该类的对象 设计一个不能被继承的类 设计一个不能被继承的类 但可以在外部环境创建该类型的对象 设计一个能被继承的类 但不能在外部环境创建该类型的对象 限制派生类对象不可以拷贝和赋值如何实现 设计一个类
  • Git(五):历史提交与远程仓库修改

    目录 4 查看提交历史 4 1 查看日志详细信息 4 2 查看某次提交信息 4 3 查看更改 5 撤销操作 5 1 取消暂存的文件 5 2 撤销对文件的修改 5 3 撤销远程记录 6 远程仓库的使用 6 1 查看远程仓库 6 2 添加远程仓
  • Python自动化部署环境

    import paramiko import sys 创建SSHClient实例对象 ssh paramiko SSHClient 调用方法 表示没有存储远程机器的公钥 允许访问 ssh set missing host key polic
  • 全球及中国冷链物流产业需求前景与投资竞争力研究报告2022版

    全球及中国冷链物流产业需求前景与投资竞争力研究报告2022版 HS HS HS HS HS HS HS HS HS HS HS HS 修订日期 2021年11月 搜索鸿晟信合研究院查看官网更多内容 第一章 冷链物流相关概述 1 1 冷链物流
  • socket传输图片之TCP协议

    在学习socket传输图片之前 我们应该先具备一些基础知识 opencv图片编码和解码 cv2 imencode cv2 imdecode 这两个函数是本教程再传输图片会用到的 个人粗浅的理解是放在内存的中的图片是以图像数据的形式存放的 而
  • 解决gomock报错:doesn‘t match the argument at index 1.

    这个问题在stack Overflow上面有一些解答 但是由于这个报错比较泛 导致仅仅看这个报错信息很难去定位写单测的时候哪里错了 我们需要使用go test v run 函数名 或者直接在vscode上面点击run 我当时发现了有下面的报
  • 逗号分隔String字符串 - 数组 - 集合,相互转换

    1 准备一个逗号分割字符串 String str 小张 小王 小李 小赵 2 逗号分割字符串转换为集合 转换为集合之前会先转换为数组 第一种 先用split将字符串按逗号分割为数组 再用Arrays asList将数组转换为集合 List
  • 暴力破解漏洞

    漏洞概述 暴力破解攻击 又叫字典攻击 是指攻击者系统地组合了所有可能性尝试破解用户的账户名 密码等敏感信息 通常使用自动化脚本或者工具进行暴力破解工具 漏洞产生的主要原因是 1 没有强制用户设置复杂密码 比如密码由数字 字母 特殊字符构成
  • 创建一个maven项目

    先图片的步骤来 2 3 4 5 6 8 9

随机推荐