MVC与MVVM
从MVVM到RxSwift
序
最近一段时间正在研究响应式编程,目前iOS开发中用的比较多的响应式编程框架有ReactiveCocoa
和RxSwift
两种。我在学习RxSwift
使用的过程中发现,关于该框架的文章大多比较晦涩,内容大多偏向于框架原理,在结合MVVM设计模式使用上却没有过多深入。本篇抛砖引玉,从MVVM解决的问题开始,逐步讲到如何将RxSwift结合MVVM使用到实际开发中。
MVC与MVVM
说MVC框架不好用是没有用好MVC框架
但是MVVM会让复杂工程的构架变得更加简单直观
MVC和MVVM其实之间并没有优劣之分,只是适用的场景不同。一门技术的出现必定是为了解决一类问题,抛开开发场景谈框架优劣就是耍流氓。
MVC解决的问题
比如,我们须要开发一个完整功能的软件,它会包含:
- 获取数据(网络请求,数据库读取,本地文件读取)
- 初始化并加载和配置视图
- 将获取的数据展示到视图上
- 将变更的数据反馈给数据库
假如没有MVC或者我偏不使用任何设计模式,将所有流程全部写在Controller中 我真是哔了狗才会这么做 ,进行编程我会怎么做:
- 写若干个获取数据(网络请求等)的方法
- 逐一初始化并配置UIKit库给定的视图控件
- 使用得到的
Dictionary
或者Array
对控件逐一赋值 - 获取TextField的文字,Switch上的Bool值,并组装成格式化的数据上传或者存储
这样的代码会出现什么问题:
- 这个文件中的代码会轻易超过1500行(代码难以阅读)
- 产品说
辛苦大家一下我们要改个小需求,于是我加了三天班;UI说亲爱的我想变一下页面布局,于是我又加了三天班(代码难以修改) - 有个视图一样但是逻辑不同的需求,我只好从源代码复制粘贴加以修改重新实现(代码难以移植)
- 出现了一个
了不得的BUG,我再加三天班(代码难以维护) - 我由于加班劳累过度住院了,接手该项目的程序员在看了代码之后立即递交了辞职报告(代码难以交接)
这种将所有代码放在一起的写法违背了程序设计时最基本的思想:高内聚,低耦合
所以MVC的出现就是为了写出模块化的程序,将模块之间的耦合降低,模块与模块的联系通过简单的接口进行暴露,Model只处理数据部分,View只处理视图部分,他们两部分在Ctroller中相互发生联系。
在Controller中规定Model何时初始化、何时赋值给View进行展示的逻辑,叫做 业务逻辑。
MVC无法解决的问题及MVVM
MVC的构架能够解决目前软件设计的绝大部分问题。
但是遇见一个功能复杂,业务逻辑繁琐的功能,即便MVC已经将它拆分成Model-View-Ctroller三部分,但Controller中的代码仍然会轻易超过800行。
所以,为了 高内聚,低耦合 ,为了代码更容易维护和修改,为了不加班, 我们需要对功能进行进一步拆分。将功能拆分为: <center>数据模型 -- 视图 -- 视图控制器 -- 业务逻辑</center>
正式因为页面业务逻辑太复杂,才使得MVC中的Ctroller过于厚重,所以我们要将业务逻辑拆分出来进行单独处理
所以MVVM这种在MVC基础上的设计模式应运而生。
MVC与MVVM之间最大的区别在于,MVC中用于管理业务逻辑的是Controller,MVVM中用于管理业务逻辑的是ViewModel。从MVC到MVVM实际上就是把MVC中Ctroller管理的业务逻辑拆分出来到ViewModel中。
如果要弄明白如何将业务逻辑拆分出来,就要先弄明白业务逻辑在代码中是以什么方式存在的,业务逻辑之于服务器是什么关系,业务逻辑之于视图是什么关系。
应用之于服务器的关系:什么是业务逻辑
服务器之于应用的关系:
- 传递数据给应用
- 接收并处理,应用传入的数据及事件
- 告诉应用什么时候启动什么事件
前两个都很好理解,这是我们写程序时的日常工作。第三个‘告诉应用什么时候该启动什么事件’都是指哪些呢:
- 基于HTTP/HTTPS响应的服务器,会告诉应用,网络请求的成功状态及请求结果
- 基于TCP/UDP长链接的服务器会将事件发送给应用,应用在客户端给出对应的状态
简化一下就是:
服务端在 某一时刻 将数据传递给应用,客户端将 数据转化为状态 展示给用户
客户端在 某一时刻 从视图 接收到用户状态 ,并转化为数据提交给服务器
数据是由Model处理的。那么 状态和时间的处理,就是业务逻辑
ViewMode与View的关系
既然业务逻辑需要处理时间和状态,并展示到视图上;同时,视图需要向业务逻辑告知自己的状态更新,因为这些状态要被转化成数据提交到服务器。
简化一下就是:
View需要调用VM的某些功能
VM也需要调用View的某些功能
这种双向操作一般我们会使用 delegate
或者block
其实在MVVM中使用这两种方式实现互绑也未尝不可,只不过有些麻烦,因为他们都是在ViewController中创建的,所以使用这两种方式的话,我们需要定义一个XXViewDelegate
和一个XXControllerDelegate
,或者使用定义block,然后将block相互赋值,或者我们可以使用Notification
...
上边的都是废话
这种双向关系使ViewModel好像View的一个映射,可以通过ViewModel获取到View的状态,也可以通过操作ViewModel操作View
这种相互响应的关系其实就是响应式关系
我们可以通过ReactiveCocoa
框架或者RxSwift
框架使View和VM之间快速建立起这种关系。
*注:那些不关乎业务逻辑的视图状态,比如动画效果等,交给视图自身去管理。
两种构架在工程中的应用原则
归根结低还是开头的那一句话:
说MVC框架不好用是没有用好MVC框架
但是MVVM会让复杂工程的构架变得更加简单直观
在同一个工程中MVC和MVVM可以混合使用,在实际客户端开发中,如何选择一个合适的构架给功能,关系到功能的开发成本,复杂度,可维护性和可移植性,设计模式的选择其实就是在这几个方面做权衡。
一般遵循的原则是:
工程第一,构架第二,代码第三
工程第一:开发时需要保证代码按期提交,不延误工程进度,且bug率低。如果时间紧张,就需要选择自己熟悉的构架来提高开发效率,尽管这套框架不是最适合这个工程的,但是工程进度比合适的构架要重要。
构架第二:在保证工程能按期完成的情况下,需要有一套合适的构架来提升工程的可维护性和可移植性。
代码第三:这是一个人的习惯问题,是一个程序员每时每刻都需要规范的行为,几乎是一种无意识行为,如何使命名变量及方法名称让代码更具有可读性,如何写注释让维护者或者将来自己维护代码更能理解编写意图。这种习惯需要时刻培养,却不能急于求成。毕竟,完成工程是眼前的事,写出的代码优不优雅,是一辈子的事。