响应式编程与MVVM架构—理论篇

前段时间,在使用了一段时间的MVVM架构以后,我从实际的项目中抽离出来,对使用MVVM架构的整个过程进行了总结,对于架构、对于编程思惟又有了不同的体会。因而提笔写下本身探索MVVM架构的经验和心得,以飨读者。

本文会先对MVC架构作一个回顾,明确MVC中各层的职责;而后会提出MVVM架构的概念,原本接下来应该顺势举几个MVVM的例子进行说明的,可是考虑到响应式编程之于MVVM的重要性,因此在举例以前会先讲解一下响应式编程的概念(出于篇幅考虑,将MVVM架构实践独立成一篇文章,想直接看实例的请移驾这里);最后会对MVC和MVVM的取舍谈谈本身的见解。话很少说,如今进入正题。

MVC架构

MVC(Model-View-Controller),是一种常见的客户端软件开发框架,具体到iOS上,绝大部分人从开始接触iOS编程的时候都被告知MVC就是事实上的默认框架。系统也为咱们实现好了公共的视图类:UIView 和控制器类:UIViewController。大多数时候,咱们都须要继承这些类来实现咱们的程序逻辑,所以,咱们几乎逃避不开MVC这种设计模式。下面就对MVC各层的职责进行明确:

Model层

Model层 是服务端数据在客户端的映射,是薄薄的一层,彻底能够用struct表征。下面看一个实例:



能够看到,Model 层一般是服务端传回的 JSON数据的映射,对应为一个一个的属性。不过如今也有不少人将网络层(Service层)归到Model中,也就是MVC(S)架构。同时,大部分时候数据的持久化操做也会放在Model层中。
总结一下,Model层的职责主要有如下几项: HTTP请求、进行字段验证、持久化等。

View层

View层是展现在屏幕上的视图的封装,在 iOS 中也就是UIView以及UIView的子类。下面是UIView的继承层级图:



View层的职责是 展现内容接受用户的操做与事件

Controller层

看了Model层和View层如此简单清晰的定义,若是你觉得接下来要讲的Controller层的定义也跟这两层同样,那你就要失望了。
粗略总结了一下,Controller层的职责包括但不限于: 管理根视图以及其子视图的生命周期展现内容和布局处理用户行为(如按钮的点击和手势的触发等)、 储存当前界面的状态(例如分页加载的页数、是否正在进行网络请求的布尔值等)、 处理界面的跳转做为UITableView以及其它容器视图的代理以及数据源业务逻辑各类动画效果等。
画风彷佛不对啊,为何Controller层的职责比其余两层加起来还多?

MVC的困境

由于MVC架构中Controller层每每代码不少,动辄二、3千行的这一特色,MVC也经常被调侃成是 Massive View Controller。形成这个问题的缘由就是MVC的定义太过简单朴素,要知道支撑一个尚不算大的企业级应用都动辄几十万行代码,还不包括各类依赖的第三方库。这么多的代码如何安置?按照传统的MVC定义,分割了小部分到Model层和View层,剩下的代码都没有其余地方能够去了,因而被通通的丢到了Controll层中。
庞大的Controller层带来的问题就是难以维护、难以测试。并且其中充斥着大量的状态值,一个任务的完成依赖于好几个状态值,而一个状态值又同时参与到多个任务中,这样复杂的多对多关系带来的问题就是开发效率低下,须要花费大量的时间周旋在各个状态值之间,对之后的功能拓展、业务添加也形成了障碍。
这样的前提下,架构的改进就显得很是有必要了。

MVVM架构初探

MVVM(Model-View-ViewModel),2005年由微软的WPF和Silverlight的架构师 John Gossman 提出,是MVP模式与WPF结合发展演变过来的一种架构框架。MVVM实质上仍是MVC架构范围,是一个精心优化的MVC架构,因此与MVC架构是兼容的。

MVVM首先将View层和Controller层进行了合并,统称为View层,由于View层和Controller层每每是一块儿出现的。而后引入了一个新的模块 — ViewModel层,ViewModel层承载的内容就是以前在Controller层中 视图展示逻辑。MVVM的图示以下:



什么是视图展示逻辑呢?在一款应用中,数据的来源多是服务端返回、数据库获取和用户输入,而后存储在Model中,可是这样的数据是一种“未经格式化的”原始数据,还不能直接显示到屏幕上。好比Model中可能有姓、名、昵称等属性,在某些界面中须要显示成"姓名"的样式,某些界面中显示成"名姓"的样式,某些界面中显示"昵称"的样式。视图展示逻辑就是把这些原始数据通过业务需求处理成展示到屏幕上的数据。能够把一个应用当作是播出一个新闻节目,Model层就是一大堆繁杂的稿件,View层就是主持人实际播报的新闻,而ViewModel层就是幕后的编辑处理团队,负责从凌乱的稿件中抽出须要的信息,整理成播报时用的稿件。这样主持人拿着整理好的稿件,就能轻松的播报新闻了。

可是呢,无缘无故多了一个ViewModel层。多一个层带来的直接问题就是信息的传递问题,层与层之间须要互通讯息,进行交流。在MVVM架构的实现中,开发人员想出了一个与传统消息传递所不同的方式,这就引出了响应式编程的概念。

响应式编程

响应式编程(Reactive Programming),是一种面向数据流和变化传播的范式。这意味着能够在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值经过数据流进行传播。举个维基百科中的例子:c:=a+b表示将表达式的结果赋给c,而以后改变a或b的值不会影响c。但在响应式编程中,c的值会随着a或b的更新而更新。



也就是说, 上图中c的值最后会是5
一样的例子还有Excel中的单元格,单元格能够包含字面值或相似"=B1+C1"的公式,而包含公式的单元格的值会依据其余单元格的值的变化而变化 。

如何实现所谓的响应式编程?在WPF中官方提供了Data Binding技术,macOS中也有相似的Cocoa Binding框架,可是在iOS中官方没有提供这样的框架。因而GitHub上出现了ReactiveCococa(如下简称为RAC)和RxSwift等优秀的第三方框架。
在RAC的思惟中,iOS上的一切都是在变化的数据流,好比输入框上用户正在不断输入的文字、被点击的按钮、旋转缩放的视图、不断改变的NSString等等,这些就像是一个"水龙头",当有变化产生的时候,水龙头就会出水,把变化传递下去,对这个变化感兴趣的人就能够在这个水龙头上套一个"水管",这我的就成为了一个接收者(subscriber),当有变化产生的时候,接收者就能从水管中拿到这个变化的具体信息。
RAC就提供了这样的"水管",可是和现实中的水管有所不一样,RAC有本身的一些限制:水管中传递的不是水,而是一个个的"玻璃球",这些玻璃球的直径和水管的内径同样大,保证了玻璃球在水管中都是依次排列经过的,这就保证了不会出现多个玻璃球并列经过的状况。更加剧要的是,在拿到玻璃球以前,能够对其进行一些个性化的定制。例如,能够在水龙头上加一个过滤嘴(filter),不符合的不让经过;也能够加一个改动装置,把球改变成符合本身的需求(map);还能够把多个水龙头合并成一个新的水龙头(combineLatest:reduce:),通过了这些定制以后,出来的符合要求的玻璃球就能拿来直接用了。

在这么强大的框架帮助下,MVVM所引入的ViewModel层和其余层之间的通信问题获得了解决。

MVC仍是MVVM?

在考虑是否要选择MVVM架构以前,先来总结一下MVVM的优点和不足。
MVVM主要有如下几个优点:
  • Controller层瘦身:将视图展示逻辑抽出到ViewModel层带来的直接变化就是Controller层变得更加轻量级,更加容易维护。
  • 更加容易测试:Controller层代码减小了,也意味着对Controller层的测试更加容易。
  • 兼容MVC:选择MVVM并不意味着彻底摒弃MVC,MVVM至关因而MVC的超集,因此和MVC是兼容的,也就是说,能够只在某一个模块中使用MVVM,不用担忧迁移架构时会形成须要全局重构的问题。
  • 解决状态以及状态之间依赖过多的问题:这个优点是由RAC所带来的,响应式编程关注的数据的变化和流向,免除了一部分的状态,而是直接将变化传到显示的控件上,实例在这里。
  • 提供统一的消息传递机制:这也是由RAC所带来的,RAC对iOS编程中大部分的实物进行了抽象,提供了统一的接口,因此能够将iOS上KVO、通知(NSNotification)、委托(delegate)、Target-Action、块(Block)等消息传递方式统一,实例在这里。
MVVM存在的问题主要有:
  • 学习曲线比较陡,一般须要引入第三方库(ReactiveCocoa/RxSwift):使用MVVM一般须要引入第三方库,并且须要转换成响应式编程的思惟方式,这是须要花费至关的学习适应时间的。
  • 建立更多的类:基本上每一个Controller类会对应有一个ViewModel类
  • 性能上有必定影响,调用栈变深:RAC的实现底层依赖于KVO,带来的问题是性能的损耗,好比光是subscribNext就慢了1个数量级,目前的回调堆栈也比较深,最简单的[signal subscribeNext^(id x){}]就会有近40次的调用。
MVVM好处很多,缺点也一堆。那到底要不要用MVVM呢?我以为,在项目还不算臃肿的时候,能够简单的对现有的MVC进行解耦优化,好比将网络层(Service层)、持久层(Storage层)等部分抽象出来便可。另外一方面,MVVM对MVC也是兼容的,能够考虑在项目中的某个模块试水MVVM,以为好再逐步替换其余模块;并且很重要的一点是,响应式编程这样一种范式至关的锻炼咱们的编程思惟,让咱们能够站在数据的变化和流向的角度去思考咱们的整一个项目,掌握这种思惟方式也能够反哺到咱们项目中别的地方。 架构没有绝对的优劣,适合本身的架构就是最好的架构,那就让咱们理性分析,拥抱变化。
相关文章
相关标签/搜索