Android中的MVC/MVP/MVVM框架模式

架构、框架及设计模式

关于架构、框架与设计模式之间的联系及区别,网上的答案五花八门,就连百度百科里的解释都很让人费解,百度百科里对“架构模式”的解释里提到MVC是一种架构模式,但在MVC的介绍里,是把MVC当成一种框架模式进行解释的,MVC是一种框架模式,但显然“框架”和“架构”不应该混为一谈。虽然百度百科本身就不具有专业性和权威性,但也从侧面反映出大家对于架构、框架以及设计模式之间的模糊认知,知乎上也有人发起提问:MVC到底是设计模式还是一种框架?架构、构架、结构、框架之间有什么区别?

本人认为《架构、框架和设计模式关系》及《架构和框架的区别》两篇文章中论述的较有说服力。当然,相比于概念而言,我们更应该关注和学习的是这些概念背后处理问题的思想。

架构应该是一个范畴最大的概念,是最高层次的设计。一个架构设计中可能会用到多个框架和多个设计模式;而框架是针对共性抽象出来的半成品,这里面可能包含着多个设计模式;而设计模式就是解决一类问题的设计思路和解决方法。——《架构、框架和设计模式关系

框架和架构的关系可以总结为两句话:(1)为了尽早验证架构设计,或者处于支持产品线开发的目的,可以将关键的通用机制甚至整个架构以框架的方式进行实现;(2)业界(及公司内部)可能存在大量可供重用的框架,这些框架或者已经实现了软件架构所需的重要架构机制,或者为未来系统的某个子系统提供了可扩展的半成品,所以最终的软件架构可以借助这些框架构造。——《架构和框架的区别

MVC

MVC全称是Model-View-Controller即模型-视图-控制器,在1979年的时候由TrygveReenskaug提出,最早用于GUI开发。MVC是一个框架模式,它强制性的使应用程序的输入、处理和输出分开。使用MVC应用程序被分成三个核心部件:模型、视图、控制器。它们各自处理自己的任务,最典型的MVC就是JSP + servlet + javabean的模式。MVC分层有助于管理复杂的应用程序,因为我们可以在一个时间内专门关注一个方面。例如,我们可以在不依赖业务逻辑的情况下专注于视图设计。同时也让应用程序的测试更加容易。

Java Web中MVC(主动型MVC)的图示如下:
MVC
Model:在MVC的三个部件中,模型拥有最多的处理任务,也是MVC的核心。模型持有所有的数据、状态和程序逻辑。模型独立于视图和控制器。

View:视图是用户看到并与之交互的界面,如就是html页面。

Controller:定义用户界面对用户输入的响应方式,负责把用户的请求转化为对 模型的操作。它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数据。

对于MVC似乎也没有明确的定义,前端、后端等不同平台的框架对于MVC的定义也不尽相同,要完全理解MVC并不是很容易。在MVC里,View是可以直接访问Model的,例如在jsp页面上,可以嵌套Java代码,这样在View上就可以越过Controller访问Model了,从而,View里会包含Model信息,不可避免的还要包括一些业务逻辑。在MVC模型里,Model不依赖于View,但是View是依赖于Model的。不仅如此,因为有一些业务逻辑在View里实现了,导致要更改View也是比较困难的,至少那些业务逻辑是无法重用的,因为这部分不是公用的Model了,其他View不能使用这个View里面的代码。而在MVC中View会从直接Model中读取数据而不是通过
Controller。所以MVC中模型与视图并没有完全分离。而且控制层和表现层有时会过于紧密,导致没有真正分离和重用。

Android中的“MVC”结构由framework给我们搭建好并提供给我们的,View层采用XML文件进行界面描述,Model大多对应于本地的数据文件或网络获取的数据体及这些数据的处理,Controller由Activity承担,控制器Activity将视图View和模型Model进行分离,并让二者在Activity中进行绑定或完成其他逻辑。但是在这套“MVC”体系中,View的功能太弱,导致我们要把处理View的逻辑写到Activity中,如Dialog的显示,导致Activity即充当了Cotroller又充当了View的部分角色,不利于测试及代码复用,所以有些人认为这套“MVC”是一个Model-View的结构。

优点

分层,结构清晰,耦合性低,大型项目代码的复用性得到极大的提高,开发人员分工明确,提高了开发的效率,维护方便,降低了维护成本。

缺点

简单的小型项目,使用MVC设计反而会降低开发效率,层和层虽然相互分离,但是之间关联性太强,没有做到独立的重用。

MVP

MVP是从MVC模式演变而来,MVP的全称为Model-View-Presenter,Model提供数据,View负责显示,Controller/Presenter负责逻辑的处理。MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会直接从Model中读取数据而不是通过 Controller。

MVP模式的图示如下:
MVP
Model:负责存储、检索、操纵数据。

View:负责绘制UI元素、与用户进行交互。

Presenter:作为View与Model交互的中间纽带,处理与用户交互的负责逻辑。

MVP模式可以让UI界面和数据分离,我们的应用至少可以分为3层,这样使得我们也可以对这三层进行独立的单元测试。MVP并不是一个标准化的模式,它有很多种实现方式。

在Android开发中,主流的思想是把Activity、Fragment作为View角色看待,如谷歌sampleMosbyAndroidMVP等,因为Activity、Fragment更像是一个ViewController,View相关的逻辑都应该放到View层而不是Presenter层,这显然是比较合理的。

但也有一部分人将Activity、Fragment当成Presenter来处理,如MVProTheMVP等。因为activity 有一个很复杂的生命周期(fragment生命周期更为复杂), 而这些生命周期很有可能对你项目的业务逻辑有非常重大的影响。 Activity可以获取上下文环境和多种android系统服务, Activity之间进行数据的传递、启动Service和执行FragmentTransaction等不应该是视图层应该涉及的领域。

优点

易于维护、易于测试、松耦合、复用性高、健壮稳定易于拓展

缺点

➣ Presenter层与View层通过接口进行交互,接口粒度不好控制。粒度太小,就会存在大量接口的情况,使代码太过碎版化;粒度太大,解耦效果不好。同时对于UI的输入和数据的变化,需要手动调用V层或者P层相关的接口,相对来说缺乏自动性、监听性。

➣ 主流的MVP是以UI为驱动的模型,更新UI都需要保证能获取到控件的引用,更新UI的时候要考虑当前是否在UI线程以及Activity的生命周期。数据是被动的通过UI控件做展示,如果数据的变化能够自动响应到UI将让我们的开发更高效。

➣ V层与P层有一定的耦合度,一旦V层某个UI元素更改,那么对应的接口可能就必须得改,数据如何映射到UI上、事件监听接口这些可能都需要转变,牵一发而动全身。

MVVM

MVVM最早于 2005 年被微软的 WPF 和 Silverlight 的架构师 John Gossman 提出,并且应用在微软的软件开发中。MVVM 在使用当中,通常会利用双向绑定技术,使得 Model 变化时,ViewModel 会自动更新,而 ViewModel 变化时,View 也会自动变化。所以,MVVM 模式有些时候又被称作:model-view-binder 模式。MVVM与MVP很相似,都将Model和View层进行了分离,只是Presenter变成了ViewModel,同时它采用双向绑定(data-binding):View的变动,自动反映在 ViewModel,反之亦然。

MVVM模式的图示如下:
MVVM

2015年Google IO 大会上,Android 团队发布了数据绑定框架(Data Binding Library),从而提供了对MVVM模式的支持。ViewModel在改变内容之后通知binding framework内容发生了改变,然后framework自动更新和那些内容绑定的view。因为数据和业务逻辑处于一个独立的ViewModel中,ViewModel只需要关注数据和业务逻辑,不需要和UI或者控件打交道。UI想怎么处理数据都由UI自己决定,ViewModel不涉及任何和UI相关的事,也不持有UI控件的引用。Data Binding帮助我们一定程度上提升了开发效率,性能更高(甚至超越手写代码)并且具有强大的表达式支持。

优点

➣ 数据驱动:在常规的开发模式中,数据变化需要更新UI的时候,需要先获取UI控件的引用,然后再更新UI。获取用户的输入和操作也需要通过UI控件的引用。在MVVM中,这些都是通过数据驱动来自动完成的,数据变化后会自动更新UI,UI的改变也能自动反馈到数据层,数据成为主导因素。这样MVVM层在业务逻辑处理中只要关心数据,不需要直接和UI打交道,在业务处理过程中简单方便很多。

➣ 低耦合:数据和业务逻辑处于一个独立的ViewModel中,ViewModel只需要关注数据和业务逻辑,不需要和UI或者控件打交道。UI想怎么处理数据都由UI自己决定,ViewModel不涉及任何和UI相关的事,也不持有UI控件的引用。即便是控件改变了(比如:TextView换成EditText),ViewModel也几乎不需要更改任何代码。它非常完美的解耦了View层和ViewModel,解决了上面我们所说的MVP的痛点。

➣ UI更新:在MVVM中,数据发生变化后,我们在工作线程直接修改(在数据是线程安全的情况下)ViewModel的数据即可,不用再考虑要切到主线程更新UI了,这些事情相关框架都帮我们做了。

➣ 团队协作:MVVM的分工是非常明显的,由于View和ViewModel之间是松散耦合的:一个是处理业务和数据、一个是专门的UI处理。所以,完全由两个人分工来做,一个做UI(XML和Activity)一个写ViewModel,效率更高。

➣ 可复用:一个ViewModel可以复用到多个View中。同样的一份数据,可以提供给不同的UI去做展示。对于版本迭代中频繁的UI改动,更新或新增一套View即可。如果想在UI上做A/B Testing,那MVVM是你不二选择。

➣ 单元测试:前面说过,ViewModel层做的事是数据处理和业务逻辑,View层中关注的是UI,两者完全没有依赖。不管是UI的单元测试还是业务逻辑的单元测试,都是低耦合的。在MVVM中数据是直接绑定到UI控件上的(部分数据是可以直接反映出UI上的内容),那么我们就可以直接通过修改绑定的数据源来间接做一些Android UI上的测试。

缺点

➣ 对于过大的项目,数据绑定需要花费更多的内存。

➣ 使用apt等技术动态生成代码,对于自定View需要借助注解写一些辅助代码才能实现绑定,View中使用的变量和具体类没有直接关联,可能要在编译的时候才能发现View中使用变量的属性名称有误。

➣ xml与ViewModel绑定,可能会影响xml布局复用性。


  讨论框架模式的目的是让我们能够从中获得启发,写出易于测试、复用率高、耦合度低、拓展性强且易于阅读的健壮型代码结构。对于设计模式我们应该理性对待,举个例子:谷歌的todo-mvp sample 中有个打开任务详情界面的实现,首先在Fragment的onTaskClick方法中调用Presenter的openTaskDetails方法,然后在Presenter的openTaskDetails方法中调用Fragment中的showTaskDetailsUi方法,从MVP的分工来讲确实合情合理,但一个简单的打开Activity界面的操作要写这么多代码可能就显得有些繁琐了。其实MV*的思想都是进行解耦隔离视图(View)和模型(Model),在实际的应用中不需要给MVC、MVP和MVVM一个明确的界限,甚至可以把几者融合在一起,无形胜有形。


参考
【1】Java中MVC详解以及优缺点总结
【2】《Android源码设计模式解析与实战》
【3】MVC,MVP 和 MVVM 的图示
【4】被误解的MVC和被神化的MVVM
【5】如何构建Android MVVM 应用框架

文章目录
  1. 架构、框架及设计模式
  2. MVC
    1. 优点
    2. 缺点
  3. MVP
    1. 优点
    2. 缺点
  4. MVVM
    1. 优点
    2. 缺点
|