前言
近期一直在看设计模式相关的资料,每逢结尾都会介绍到 MVC 模式,而最近比较火的却是 MVP 和 MVVM ,更有甚者,将 MVP 和 MVVM 结合起来形成了 MVPVM ,大神的造诣总是让俺望其项背,下面就结合自己的学习,浅析 MVP。
从哪里开始学习呢?当然是从 Google 自身开始咯,Google 推出了自己的官方 MVP 架构例子。源码地址
相关翻译
今天就主要来看看官方写的最基础的 MVP 的例子 todo-mvp。本文不打算讲解里面代码的具体实现,只是从宏观角度看一下,Google是如何实现 MVP 模式的,细节需要自己慢慢体会,每个人的体会都会不一样的。(里面还涉及到了测试相关的东西)
剖析
Step One
首先从 Github 上 clone 下来,然后本地运行起来,效果如下
这是一个简版的便签,相关数据存储在本地数据库中。知道这个 App 是干嘛的,对下面分析相关代码就好理解了。
Step Two
看代码,找关系,官方的代码结构如下
看目录
- addetitask:添加/修改任务功能
- statistics:分析功能(位于侧边栏上)
- taskdetail:查看任务详情
- tasks:首页界面,展示任务列表
- data:数据操作相关
- util:工具类相关
通过目录,这个 App 的相关操作也就一目了然了,接下来就针对 task 目录下的代码进行分析,看看是如何体现 MVP 思想的。
Step Three
分析前,先认识一下 MVP ,至少了解一下代表的含义
MVP (Model-View-Presenter) 该模式包含以下几个方面
- View:负责绘制 UI 元素、与用户进行交互(Activity/Fragment);
- View interface:需要 View 实现的接口,View 通过 View interface 与 Presenter 进行交互,降低耦合,方便进行单元测试;
- Model:负责存储、检索、操纵数据(有时也实现一个Model interface 用来降低耦合);
- Presenter:作为 View 与 Model 交互的中间纽带,处理与用户交互的负责逻辑。
先掌握上面的基础概念就可以,下面进入正题
Step Four
task 目录下包含了以下几个文件
- ScrollChildSwipeRefreshLayout:自定义控件,扩展了 SwipeRefreshLayout
- TasksActivity:程序入口
- TasksContract:关联了 View 和 Presenter,是个接口
- TasksFilterType:是个枚举类,里面定义了 Tasks 列表的状态;
- TasksFragment:展示 Tasks 列表等相关操作的 Fragment;
- TasksPresenter: Presenter,负责关联 View 和 Model
它们的关系用类图来表示,如下图所示
其中数据类的相关 java 类简化表示了,详细的自行查阅 Github 上的内容
从上面可以看到,Google 首先定义了两个接口文件 BaseView 和 BasePresenter,然后又通过 接口 TasksContract 进行关联,所有用到上面两个接口的都是通过 TasksContract 进行管理。
看上面的图,好像有点繁杂,眼花缭乱的感觉,那就先搬一张 MVP 模式基本的类图,如下
那么结合上图,怎么根据上面进行划分呢,其实非常容易,如下
上面标的还不算特别好,其中 BaseView 也属于 View 里面,BasePresenter 属于 Presenter 范畴
如果大家仔细看的话,就会发现,View 和 Model 直接是没有任何关联的哦,而 Presenter 和 Model 和 View 都是有关联的,现在在和上面标准的类图进行对比,是不是就一目了然啦。如果大家对里面的代码细节感兴趣,请自行查阅源码哦。
对了,说一点,里面的 TasksActivity 的作用就是将 Presenter 和 TasksFragment 关联起来(不贴点代码好像也不行了)
TasksFragment tasksFragment =
(TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame);
if (tasksFragment == null) {
// Create the fragment
tasksFragment = TasksFragment.newInstance();
ActivityUtils.addFragmentToActivity(
getSupportFragmentManager(), tasksFragment, R.id.contentFrame);
}
// Create the presenter
mTasksPresenter = new TasksPresenter(
Injection.provideTasksRepository(getApplicationContext()), tasksFragment);
// Load previously saved state, if available.
if (savedInstanceState != null) {
TasksFilterType currentFiltering =
(TasksFilterType) savedInstanceState.getSerializable(CURRENT_FILTERING_KEY);
mTasksPresenter.setFiltering(currentFiltering);
}
Step Five
再来审视一下 MVP
- M:Model,数据相关,主要就是提供数据的存取功能,像一个数据仓库。Presenter 需要通过 Model 层存、取数据。
- V:View,用户界面,也就是Activity、Fragment、View控件等,它持有 Presenter 的一个成员变量。通常 View 需要实现一个逻辑接口(示例就是这样做的),将 View 上的操作会转交 Presenter 来年实现。最后,Presenter 调用 View 的逻辑接口将结果返回给 View 元素。
- P:Presenter,交互中间人。沟通 Model 和 View 的桥梁,将 Model 和 View 之间的关联斩断掉,改为 Presenter 进行联络。这也是和 MVC 模式最大的不同。
MVP 的优点
- 看类图就知道,首先就是分离了 View 和 Model,降低了耦合
- 易于维护、测试,一目了然,接口的存在,大大降低了测试的复杂度
- 代码的可阅性就变的容易了
- 类型的职责明确,各司其职,应用 MVP 时,会感觉到,结构清晰明了,爽
说完优点,肯定有缺点的,谁让没有一个模式都是金光闪闪,毫无缺点的。
- Presenter 负责沟通 Model 和 View ,当然不可避免的就会“臃肿”起来
- 有 View 的存在,就会有 Presenter 的存在,而在实际项目中,一个界面往往有很多的 View 构成,那么想想也是很“激动”的
最后
每个模式的出现,都会解决其他模式的缺点,却又不可避免的带来自身的缺点,然后等着下一模式的出现来解决上一模式的缺点,周而复始。也正是这样的循环,让我们不断的向更好的方向进步,例如: MVVM 的出现。
想要深入学习,查看具体实现方式,请自行下载相关代码进行研究。
参考资料
Android开发艺术探索[M]. 电子工业出版社, 2015.487-494