MVP

学习思维三部曲: WHY - HOW - WHAT, 通过现象看本质.

  • WHY 为什么

  • HOW 怎么做

  • WHAT 是什么

我将跟着这三个问题来阐述MVP为何物, 以及为何要引入MVP. 并在最后扩展说明MVP在项目中的应用.

WHY

为什么要引入MVP模式?

我们先来看看以前的Activity/Fragment是如何写的:

  1. 初始化View

  2. 维护View的状态

  3. 控制程序的逻辑, 处理网络或者本地的数据.

  4. 发送网络请求

  5. 等等

这简直就是无所不能的上帝类. Activity/Fragment承载力过多的职责, 导致在开发途中Activity/Fragment会越来越臃肿庞杂, 维护, 扩展变得举步维艰. 相信大家都遇到过这种状况. 类越小, bug越不容易出现, 越容易调试. 类越大, bug越容易出现, 出现bug也越难调试, 以至于生活都无法自理. 因此为了尽可能的避免这种情况的发生, 引入了MVP模式.

HOW

如何使用MVP模式? 具体代码我就不贴了, 可以参考这篇博客 架构探险——Android MVP模式浅析.

实现MVP之中很重要的一点, 也是与MVC最大的区别就是Presenter不持有View的引用, 而是持有View的接口, 并且View不与Model直接通信, 而是通过Presenter来衔接View与Model.

WHAT

接下来我们看看MVP是什么? 要了解MVP是什么, 首先了解下面概念:

  • 软件中最核心的, 最基本的东西是数据. 我们写的代码都是围绕数据的.

  • 随着数据的产生, 修改等变化, 出现了业务逻辑.

  • 随着数据的展示,出现了不同的界面技术.

MVP是针对有UI界面的软件/应用程序, 主要用于将UI展示与业务逻辑分开的一种设计模式.

MVP分别对应着Model, View, Presenter:

  • Model - 为应用程序提供数据, 如REST Api, db, SharedPreferences等.

  • View - 负责处理应用程序的UI显示以及响应用户事件并反馈处理结果. 在Android中通常是Activity或者是Fragment, 甚至可以是一个自定义View.

  • Presenter - 整个应用的控制中心, 控制着各种逻辑的分发. 例如View层接收到用户的点击事件, 将事件转发给Presenter, Presenter对事件一些简单的处理后转发给Model层, 并从Model层拿到处理结果并给View层显示.

MVC自诞生以来衍生了无数变种, 其中包括经典的MVP与MVVM. MVP经过这么多年的发展也演变出各种版本, 运用最广泛的两种非Supervising Controller与Passive View莫属. 而Passive View是目前最受欢迎的变种. CoreLibs项目中也是使用的Passive View.

Passive View

Passive View起初是为了满足自动化测试而诞生的. 众所周知View是很难测试的, 因为你不仅要测试View的结构样式, 还要模拟用户的交互行为. 由于MVP中的Presenter是可测试的, 因此将UI界面提炼抽象成接口, 并将UI逻辑也交由Presenter处理. 还将View与Model之间基于观察者模式建立的映射关系切断.

MVP Passive View 中View这一层是很轻薄的一层, 只需要负责给用户显示数据, 并且转发用户的事件, 处理一些简单的UI逻辑, 整体的控制权是全部后移转交给Presenter的. 并且View在MVP中代表的是一个接口, 一个将UI界面提炼而抽象出来的接口. 接口意味着任何实现了该接口的界面, 都能够复用已有的Presenter和Model代码. 这在界面变化频繁的如今是有很大优势的.

Passive View的优势

  1. 模型与视图完全分离, 我们可以修改视图而不影响模型.

  2. 可以更高效地使用模型, 因为所有的交互都发生在一个地方——Presenter内部

  3. 我们可以将一个Presener用于多个视图, 而不需要改变Presenter的逻辑. 这个特性非常的有用. 因为视图的变化总是比模型的变化频繁.

  4. 如果我们把逻辑放在Presenter中. 那么我们就可以脱离用户界面来测试这些逻辑.

  5. 开发人员可以专注于应用逻辑的开发, 到时候UI界面只需实现相应的接口.

想了解更多的MVx系列历史的可以参考系列 UI架构小史.

MVP的缺陷

说了这么多优点, 下面来说说缺陷:

  1. 类的数量急剧增多. 每创建一个Activity需要创建与与其对应的View接口, Presenter, Model接口以及实现.

  2. MVP是一个方法论的东西,没有固定的实现方式.

  3. View持有Presenter引用, Presenter持有View和Model接口, 会导致多出许多初始化对象, 以及必要的销毁对象的重复代码.

  4. 额外的学习曲线

首先使用MVP模式类的数量急剧增多, 这个是无法避免的. 任何一个架构都可能有这个缺陷. 其次, 为了理解MVP的思想并将其运用到项目中去, 是需要额外的学习. 我相信学习不是坏事, 肯定是利大于弊.

CoreLibs则专门对剩下两个问题做了优化处理:

MVP是一个方法论的东西,没有固定的实现方式.

MVP作为展示层的一种编程范式, 并且因为它是方法论的东西, 对实现方式并没有固定的形式, 所以会被滥用, 如果没有深刻理解MVP的思想, 更加会导致灾难性的结果.

有时候项目难以维护的原因之一是由于一段代码出现在了不该出现的位置. 有可能每个人对于MVP的理解不一样, 对层次之间的职责划分理解不同, 就会出现我认为应该是Model层的代码, 你却写在了Presenter里的情况. 导致项目的代码组织看起来比较混乱, 造成反面效果.

那么如何避免这种情况的出现呢? 定义好各个层次之间的职责! 我们需要明确的, 清晰的职责划分规范, 这样在使用MVP的过程中才能有据可循. 遇到难以把握的地方也可以有个参考指导.

在分层架构中对需求对业务逻辑的判定是很重要能力之一, 比如什么样的逻辑是业务逻辑, 从而需要放入Model层? 什么样的逻辑又是UI逻辑, 是放在View中还是交由Presenter管理? 这些问题只能通过不断的编写代码, 累积经验来判定.

因此一份职责划分的说明文档必不可少. 我在后续篇幅中会总结一下我目前遇到的职责划分的问题, 具体可参考 - 职责划分 一章 (还在不断完善中).

View持有Presenter引用, Presenter持有View和Model接口, 会导致多出许多初始化对象, 以及必要的销毁对象的重复代码.

解决这个问题可以使用依赖注入框架如Dagger. 但是考虑到Dagger本身的复杂性, 以及还是有一些重复代码, 我选择使用 - 加入模板方法 来达到减少重复代码的目的. 具体可以参考 MVP在CorLibs中的应用.

Last updated