MVP模式是你的救命稻草吗?

简介:

为什么要学习架构?

不管是MVC还是MVP,亦或则其他架构,它们的设计目的都是为了达到编码的最高境界,那就是:低藕合,高复用,易测试,好维护。

而要达到这个终极目标,首先要理解的是每个部分各自负责些什么,以及如何组合在一起。因此我个人认为,学习架构关键在两步:

  1. 如何把缠在一起的代码拆分
  2. 如何把拆开的代码再组合

很多新手在刚做项目时,都会把所有的代码,如数据的访问和处理,数据的展示,用户的输入都写在一起,代码和思维都呈现出一种线性的形式,一切都是直来直往的。这样代码量确实少,写的时候也觉得方便,但是带来了几个致命的问题:

  1. 当业务越来越复杂时,修改代码成了噩梦。同样的逻辑放在不同的地方,本来只用改一处的变成了需要改几百处。而又因为所有的逻辑都互相牵扯住,导致本来只想改一处的,结果却影响了几百处。
  2. 当时间越来越遥远时,理解代码成了噩梦。本来只需要阅读几行的时候,却因为所有的代码都杂在一起变成了要阅读几千行。时间一长,重新阅读时,别说别人,就是自己也很难一下就能掌握关键点。
  3. 当需要做一个功能时,却发现代码无法复用,明明是一样的逻辑也只能靠Ctrl+CCtrl+V。这又为今后修改代码时增加了工作量。
  4. 当需要测试时确发现很难进行测试,每次修改一处代码后都必须进行重复的人工测试,无法进行自动化测试,模块和模块也无法拆开来进行独立的单元测试,每次都要整体的测一遍才能确保一切完好如初。

要换的不是架构,而是思维方式

其实目前市面上的架构模式已经有很多种,各有不同,但模式终究只是一种设计理念的表现形式,学习再多的架构,你也只是多会用了几种工具而已,学习一种模式其实是在学一种思维方式:

如何在解决问题的时候把问题合理的拆分,又如何将拆分的零件合理的组装成解决问题的工具。

将这种思维方式深入到你的大脑里,血液里,直至骨髓,让它成为你思考问题的一种习惯和定式,逐渐成为你的一部分,那么这时你已达到无招胜有招的境界了。

闲话先不扯了,回正题。

实现架构没有唯一标准

这里首先需要说明的是无论MVP或MVC还是MVVM等任何一种架构和模式其实都没有谁优谁劣之分,而且就算是同一种架构,也可以根据不同的使用场景来做不同的实现方式,这里并没有宇宙绝对的对错标准和概念定义。这和张三丰在教无忌太极拳以后让其先忘掉招式是一样的道理,在应用型领域,定式和概念只应是在学习的过程中才存在的,而真正学会以后应该马上忘掉定式,忘掉概念,将其用熟用活才是关键。

所以说我在此描述的概念也不是唯一的标准,而只是个人对其的理解。

什么是MVC

因为MVP是MVC的一个变种,因此我们先来看在MVC里代码是如何拆分和组合的,这里简要的描述常见的定义:

  1. 拆分:Model负责数据,View负责显示,Controller负责用户输入。
  2. 组合:View将用户操作和事件传递给Controller,Controller将用户命令翻译成消息来传递给Model,Model在处理完数据后,通知View来显示给用户,而View又从Model取出数据。

我认为在学习MVP之前,必须深刻理解什么叫做MVC,因为两者的区别其实没有你想象中的那么大,与其神话并盲目追崇新的架构,期望其能解脱你于苦海,还不如深刻的理解MVC的设计理念,这就和学习一门新语言一样,只有你真正深刻的理解了其思维方式,那么学习新的一门语言其实都是易如反掌的。因为它们其实都是在做一件事,只是做的过程上有些许差异而已。

更多MVC的理解,可以参考我之前写的一篇文章:在谈MVP之前,你真的懂MVC吗?

什么是MVP

MVP与MVC最大的区别就在与将Model和View通过Presenter隔开了,不再允许其互相直接通信,而所有的消息都是通过Presenter这个中间人来传递。

而这样做的目的主要是为了将数据和展示划出更明确的界限。

首先我们来看MVP到底是在解决什么问题的:

  • 数据管理
    1. 什么是数据?(Model)
    2. 如何筛选数据?(Selections)
    3. 如何改变数据?(Commands)
  • 用户界面
    1. 如何展示数据?(View)
    2. 如何通过事件来改变数据?(Interactor)
    3. 如何将这些放在一起?(Presenter)

可以看出其实一个GUI程序的核心,还是在于用户和数据之间的关系。而架构的引入也是为了解决这几个核心的问题。

那么MVP又是如何解决这几个问题的,Model,View,Presenter三者又各自负责什么呢?谁来负责请求网络数据,访问和存储本地数据,谁来负责处理数据,谁来负责显示数据,谁又来负责和用户交互呢?

具体表现在代码上,也可以说:网络请求应该放在哪,数据库访问应该放在哪,业务逻辑应该放在哪里,处理用户输入应该放在哪,谁又来控制显示或隐藏View的等具体的细节问题。

带着这些具体问题,我们一起来学习。

如何拆分

首先来看MVP各自负责什么:

  1. Model,负责定义数据(解决什么是数据)
  2. Presenter, 负责在Model和View之间,从model里取出数据,格式化后在View上展示(解决如何把数据和用户界面放在一起)。
  3. View,负责担任一个被动界面,用于展示数据。(解决如何展示数据)

和MVC比较而已,这里出现一个最大的疑问就是:那么谁来负责和用户的操作交互呢?答案是,用户在View上的所有操作(事件)都由View路由给Presenter,由Presenter来与其交互。

而和MVC最大的区别在于Model和View完全被Presenter隔开了,Presenter作为它们之间的中间人去传递所有数据。

如何组合

三者又是如何组合起来的呢?

很显然Presenter作为中间者,它是同时拥有View和Model的引用的,为了在它们之间起到桥梁作用,即Presenter会主动和View和Model进行通信

Model和View必须是完全隔离的,不允许两者之间互相通信,保持对彼此的不感知,这样的好处是你彻底将数据和展示分离来开,并且可以独立的为Model去做测试。

Model在三者中是独立性最高的,Model不应该拥有对View的引用,而且Model也不需要保存对Presenter的引用,对于Presenter而已,Model只需要提供接口,等着Presenter来调用时返回相应数据即可,这和经典MVC模式中是非常不同的,在MVC中Model在数据发送变化后,是需要发送广播来告之View去更新用户界面的,而在MVP中,Model是不应该去通知View,而是通知Presenter来间接的更新View的。

Presenter和Model的关系也应该是基于接口来通信,这样才能把Model和Presenter的耦合度也降到最低,那么在需要改变Model内部实现,甚至彻底替换Model的时候,Presenter则是无需随之改变的。这样做带来的另一个好处就是你可以通过Mock一个Model来对Presenter以及View做模拟测试了,从而提高了可测试性。

那么View和Presenter的关系呢?View是需要拥有对Presenter的引用,但仅仅是为了将用户的操作和事件立即传递给Presenter,为了让View和Presenter耦合较低,View也只应该通过接口与Presenter通信,从而保证View是完全被动的,一方面它由用户的操作触发来和Presenter通信,另一方面它完全受Presenter控制,唯一需要做的事情就是如何展示数据。

简要总结三者之间的关系是:View和Model之间没有联系,View通过接口向Presenter来传递用户操作,Model不主动和Presenter联系,被动的等着Presenter来调用其接口,Presenter通过接口和View/Model来联系。

View <- 接口 <- Presenter ->接口 -> Model

View -> 接口 -> Presenter <- 接口 <- Model

Talk is cheap, show me the code

为了便于理解,这里提供一些伪代码加注释演示一下如何实现MVP模式:

View

interface IUserView {

  void setPresenter(presenter); void showUsers(users); void showDeleteUserComplete(); void showDeleteUserError(); } class UserView implements IUserView { UserPresenter presenter; // 保持对Presenter的引用,用于路由用户操作 void setPresenter(presenter) { this.presenter = presenter; } // 将Presenter传递来的数据展示出来 void showUsers(users) { draw(users); } // Model操作数据成功后,通过Presenter来告之View需要更新用户界面 void showDeleteUserComplete() { alert("Delete User Complete"); } // Model操作数据失败后,也是通过Presenter来告之View需要更新用户界面 void showDeleteUserError() { alert("Delete User Fail"); } // 当用户点击某个按钮时,将用户操作路由给presenter,由presenter去处理 void onDeleteButtonClicked(event) { presenter.deleteUser(event); } }

Model

interface IUserModel {

  List<User> getUsers(); boolean deleteUserById(); } class UserModel implements IUserModel { // 在数据库里查找数据,并将数据返回给presenter List<User> getUsers() { return getUsersInDatabase(id); } // 在数据库里删除数据,并将结果返回给presenter User deleteUserById(id) { return deleteUserByIdInDatabase(id); } }

Presenter

interface IUserUserPresenter {

  void deleteUser(event); } class UserUserPresenter implements IUserPresenter { // 保持对View的引用 IUserView view; // 保持对Model的引用 IUserModel model; UserUserPresenter(IUserView view, IUserModel model) { this.view = view; this.model = model; this.view.setPresenter(this); } void start() { // 从Model中取出数据 List<User> users = model.getUsers(); // 将数据发送给View,让其展示到用户界面 view.showUsers(users); } void deleteUser(event) { // View将用户操作路由过来,由Presenter来处理 long uid = whichUserNeedToDeleteBy(event); // 将用户操作翻译成命令或消息传递给model,以改变数据 boolean success = model.deleteUserById(uid); // 将Model操作数据后的结果通知View来改变用户界面 if (success) { view.onDeleteUserSuccess(); } else { view.onDeleteUserFail(); } } }

OK,到此一个最简单的MVP模式就实现了,可以看到整个架构的轮廓已经很清晰了,而且面向接口编程也带来了很多的好处,让代码之间藕和较少,对测试支持也更为友好,理解和维护起来也更加方便了。

以后有时间再为大家描述如何在android里面实现MVP模式,以便更具体的理解。有疑问欢迎参与讨论,大家一起学习进步。

参考文档

  1. Model–view–presenter WIKI
  2. MVP: Model-View-PresenterThe Taligent Programming Model for C++ and Java

    本文转自 一点点征服   博客园博客,原文链接:http://www.cnblogs.com/ldq2016/p/6694415.html ,如需转载请自行联系原作者


相关文章
|
6月前
|
自然语言处理 监控 搜索推荐
佛萨奇2.0/3.0Metaforce原力元宇宙系统开发步骤逻辑丨方案项目丨案例设计丨成熟技术丨源码功能
佛萨奇(VoZaChi)是一个虚拟助手系统,其发展至2.0和3.0版本会有一些特定需求和逻辑
|
前端开发 网络协议 测试技术
业务架构的救世主是 MVI(一)
业务架构的救世主是 MVI(一)
8930 0
|
存储 缓存 前端开发
“无架构”和“MVP”都救不了业务代码,MVVM能力挽狂澜?(一)
“无架构”和“MVP”都救不了业务代码,MVVM能力挽狂澜?(一)
107 0
|
存储 前端开发 网络协议
“无架构”和“MVP”都救不了业务代码,MVVM能力挽狂澜?(二)
“无架构”和“MVP”都救不了业务代码,MVVM能力挽狂澜?(二)
114 0
|
存储 缓存 前端开发
“无架构”和“MVP”都救不了业务代码,MVVM能力挽狂澜?(二)(下)
“无架构”和“MVP”都救不了业务代码,MVVM能力挽狂澜?(二)
93 0
|
安全 前端开发 网络协议
MVP 架构最终审判 —— MVP 解决了哪些痛点,又引入了哪些坑?(三)
MVP 架构最终审判 —— MVP 解决了哪些痛点,又引入了哪些坑?(三)
157 0
|
存储 缓存 前端开发
MVP 架构最终审判 —— MVP 解决了哪些痛点,又引入了哪些坑?(二)
MVP 架构最终审判 —— MVP 解决了哪些痛点,又引入了哪些坑?(二)
113 0
|
设计模式 网络协议 前端开发
MVP 架构最终审判 —— MVP 解决了哪些痛点,又引入了哪些坑?(一)
MVP 架构最终审判 —— MVP 解决了哪些痛点,又引入了哪些坑?(一)
91 0
|
负载均衡 安全 测试技术
洽谈区块链游戏项目系统开发逻辑讲解方案及技术参考原理
洽谈区块链游戏项目系统开发逻辑讲解方案及技术参考原理
289 0
互联网创业模式
互联网创业模式
116 0