也谈MVP

Most Valuable Player ?

lol,你以为你猜对了吗?其实这里的MVP指的是一种软件设计模式,MODEL-VIEW-PRESENTER 是一种分层设计的思想。可是为什么是谈MVP呢?因为MVP已经火遍了大江南北,网络上相关的文章已经不计其数。而我只想提出自己的一些见解和思考。

悄悄火起来的MVP

在MVP出现之前,比较经典的分层架构就是MVC。MVC已经被使用了很多年,并且被证明是一个非常好的分层设计模型。可为什么又会出现奇葩的MVP呢?这是因为在程序越来越大的时候,部分程序员发现代码维护变得困难起来。为什么会这样,看图:

MVC

可以看到View可以直接通过Model层获取数据,也可以间接通过Controller获取数据,这就造成了View层既有直接访问Model层的代码,也有间接通过Controller访问Model的代码,在代码量较大的情况下就造成了维护困难的窘境。MVP正是意识到了这个问题,才出现了使用Presenter层间接访问Model的解决方案,继续看图:

MVP

简单来说,MVP的三层架构基本如上图。不过,你从网上看到的图可能和上图不一样。网上的图其中还会有一根线从Presenter层指向View。这并不代表Presenter要直接去访问View。这只是说Presenter需要和View之间保持同步。View的变化要反映到Presenter中,而Presenter的变化也反映到View中,二者之间通常使用回调的方式保持同步。

在Android应用架构中,MVC和MVP对于View的观点也不一致。MVC三层架构将Activity/Fragment视为Controller层,View层为布局相关的View控件。而MVP三层架构中则将Activity/Fragment视为View层,Presenter层独立的抽象层。

关于MVP将Activity/Fragment视为View层一直存在很大的争议,了解更多关于MVP争议的文章,请关注文章最后的参考文档。

那么,为什么说MVP的设计就解决了代码混乱、不易于维护的问题了呢?
从图中可以看到,MVP三层架构中,View层所有的数据访问都必须经过Presenter,这样所有关于数据访问的代码都在Presenter中呈现,从而极大地解决了维护成本问题。

MVP设计的另外一个比较大的优点,就是:它解决了Android应用一直以来单元测试困难的问题。通过MVP模型,我们可以直接对Presenter层写单元测试,Presenter层是纯Java代码,不需要牵扯到Android层,可以非常方便地进行单元测试,减少Bug的发生。

不过话说回来,中国程序员是很少写单元测试的, lol。

那么,MVP的设计是否就没有缺点了呢?答案是:当然不是!

MVP的演进

虽然MVP极大地解决了:

  • 代码维护困难
  • 单元测试困难

的问题。
但在大型应用项目中,仍然有可能造成Presenter层代码量巨大,而难以维护,有什么好的办法解决这个问题呢?
于是,MVP Clean模型诞生了!
MVP Clean模型其实还是MVP模型,只是将Presenter的数据操作再进行了一定的抽象,增加了Domain(以下简称为Interactor)层,Interactor层专门用于与Model进行交互,Presenter不再直接与Model进行交互。这真的可以解决Presenter层代码臃肿的问题吗?答案是:可以!
有人肯定已经有了一个疑问,这不过是挂羊头卖狗肉而已,把Presenter层的代码迁移到了Interactor。其实并不是这样,Presenter访问Model的代码可能非常大,这样可以将Interactor拆分为多个,即一个Presenter对应多个Interactor进行不同功能的数据访问,单元测试就转化到对单个Interactor进行测试即可。由此可见,这的确解决了Presenter层代码臃肿的问题。

MVP Clean模型的基础上,是否还有优化的空间呢?答案是:有。

在上文的MVP演进中,仅仅是对Presenter层进行了优化。对于Model层,其实还有进步的空间。对于Model层比较经典的做法就是使用Repository模式进行数据的封装。对于Domain层只暴露对应的接口,Domain层并不知道Model是什么数据,是来自网络,还是数据库,还是缓存... 即保证了实现可以随时替换的可能。

这里在设计Model层的时候,一定要切记勃兰特·梅耶的OCP(开闭)原则。即:要保证充分的可扩展性,但要避免直接修改源码。

到这里为止,对MVP的封装基本可以告一段落。可是,还有一个比较棘手的问题。Presenter和View层的同步问题,如何解决?
Activity/Fragment都有自己的生命周期,Presenter也必须保证其生命周期与Activity/Fragment同步。关于这个问题,网络上有几种解决方案。其中,最容易想到的就是利用Activity/Fragment本身的生命周期状态保存恢复方法。这种方式的确可以解决这个问题,却带来了额外的维护成本。于是,使用Android提供的Loader类进行Presenter生命周期维护的方法就应运而生了。为什么可以做到生命周期的维护,这里就不赘述了,请大家点击文章最下方的参考文档列表进行查看。

MVP整体设计

通过上面的讨论,我们对MVP的整体设计如下:
1) 数据流向: UI -> Presenter -> Interactor -> Repository -> Data
2)Presenter对象创建:使用Loader创建

基于这样一个结构,我们来看一下完成登录功能可能需要的类:

登录

天哪!仅仅完成一个登录功能,居然需要至少7个类。而且,还不包括框架类。

直到这里,我想MVP的缺点也暴露无遗了!
是否有办法减少过多的类呢?答案是:有!
我们来看一下谷歌官方的解决方案:
iosched : 这是谷歌IO大会官方APP

iosched

对这个工程简单分析就知道,谷歌使用了一个比较巧妙的方法维护Presenter的生命周期,即使用一个冗余的Fragment作为Presenter的实现类。同时,Presenter类仅仅是设置了一些属性而已。正在的数据查询操作放在了Model类中去处理。
对于View接口,它也使用了一个统一的UpdatableView进行处理。

总结一下:谷歌使用了Fragment作为Presenter实现,所有的UI类都依赖这同一个Presenter实现,对于View接口,也使用了统一的封装UpdatableView。这样,在某个功能模块里面,其实只需要实现相应的Model即可,极大地减少过多类的问题。

咋一看,禁不住要为谷歌如此碉堡了的设计鼓掌。可仔细一想,谷歌的这种方式,并不适用于所有情况。谷歌的这个app仅仅设计到数据的存取,逻辑相对简单。对于一个复杂功能的app,很难对Presenter和View进行统一封装。所以,这也是目前MVP设计的窘境。即:你可以这样设计,但你却很难确保你的团队都能理解你的设计,都能追随你的设计,这恐怕要一个标准的规范文档才可以做到比较统一。

关于MVP设计的思考

MVP设计模式解决了极大一部分困扰程序员的问题,但也带来了过度封装的嫌疑。对于不同规模的app,一定要学会适当取舍;同时,也要学会对MVP进行二次封装,减少开发成本。对于MVP存在的问题,谷歌也提出了自己的解决方案,即:Data Binding。Data Binding是一种模板设计模式,被广泛用于Web开发中。谷歌的这种做法,也让我们相信天下大势,必趋一统。Data Binding解决了一部分MVP问题,也带来了一些新的问题。由此看来,我们离规范的Android应用架构设计依然很遥远,让我们一起期待这一天早日到来。

说出你的想法

如果你对MVP设计模式有自己独到的见解的话,请在文章下方写下你的评论告诉我,我期待着与你一起探讨,/WX。

参考文档

The Clean Architecture
MVP for Android: how to organize the presentation layer
Introduction to Model View Presenter on Android
An Introduction to Model View Presenter on Android
Android MVP 详解(上)
Android MVP 详解(下)
iosched
android-architecture

注:如果你喜欢我的文章,别忘了点击这里 打开页面,点击屏幕左侧的添加关注按钮关注我哦。如果你觉得这篇文章对你有帮助,也希望赏点酒钱哦!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,478评论 25 707
  • 作者:李旺成 时间:2016年4月3日 “Android MVP 详解(下)”已经发布,欢迎大家提建议。 MVP ...
    diygreen阅读 128,815评论 86 1,321
  • 转载至:http://www.jianshu.com/p/9a6845b26856 “Android MVP 详解...
    SnowDragonYY阅读 10,319评论 5 241
  • 目前已转至个人博客,本系列地址:Lam's Blog - Knowledge as Action 前言 本文基于周...
    格子林ll阅读 9,415评论 1 76
  • 我不想时光飞逝 我不想世事沧桑 我不想你容颜易改 我不想我忘记以往 我走过时光 也曾历尽沧桑 我寒夜起床 只为记起...
    晨尘辰阅读 373评论 3 11