关于iOS中MVC和MVVM的区别和联系

MVC 的历史

MVC,全称是 Model View Controller,是模型 (model)-视图(view)-控制器 (controller) 的缩写。它表示的是一种常见的客户端软件开发框架。

MVC的概念最先出如今二十世纪八十年代的 施乐帕克 实验室中(对,就是那个发明图形用户界面和鼠标的实验室),当时施乐帕克为 Smalltalk 发明了这种软件设计模式。

如今,MVC 已经成为主流的客户端编程框架,在 iOS 开发中,系统为咱们实现好了公共的视图类:UIView,和控制器类:UIViewController。大多数时候,咱们都须要继承这些类来实现咱们的程序逻辑,所以,咱们几乎逃避不开 MVC 这种设计模式。

可是,几十年过去了,咱们对于 MVC 这种设计模式真的用得好吗?其实不是的,MVC 这种分层方式虽然清楚,可是若是使用不当,极可能让大量代码都集中在 Controller 之中,让 MVC 模式变成了 Massive View Controller 模式。

Controller的臃肿问题何解?

不少人试图解决 MVC这种架构下 Controller 比较臃肿的问题。我还记得半年前 InfoQ 搞了一次移动座谈会,当时 BeeFramework 和 Samurai-Native 的做者 老郭 问了我一句话:「什么样的内容才应该放到 Controller 中?」。可是当时由于时间不够,我没能展开个人观点,此次正好在这里好好谈谈我对于这个问题的想法。

咱们来看看 MVC 这种架构的特色。其实设计模式不少时候是为了 Don't repeat yourself 原则来作的,该原则要求可以复用的代码要尽可能复用,来保证重用。在 MVC 这种设计模式中,咱们发现 View 和 Model 都是符合这种原则的。

对于 View 来讲,你若是抽象得好,那么一个 App 的动画效果能够很方便地移植到别的 App 上,而 Github 上也有不少 UI 控件,这些控件都是在 View 层作了很好的封装设计,使得它可以方便地开源给你们复用。

对于 Model 来讲,它实际上是用来存储业务的数据的,若是作得好,它也能够方便地复用。好比我当时在作有道云笔记 iPad 版的时候,咱们就直接和 iOS 版复用了全部的 Model 层的代码。在创业作猿题库客户端时,iOS 和 iPad 版的 Model 层代码再次被复用上了。固然,由于和业务自己的数据意义相关,Model 层的复用大多数是在一个产品内部,不太可能像 View 层那样开源给社区。

说完View和Model了,那咱们想一想Controller,Controller有多少能够复用的?咱们写完了一个 Controller 以后,能够很方便地复用它吗?结论是:很是难复用。在某些场景下,咱们可能能够用addSubViewController 之类的方式复用 Controller,但它的复用场景仍是很是很是少的。

若是咱们可以意识到 Controller 里面的代码不便于复用,咱们就能知道什么代码应该写在 Controller 里面了,那就是那些不能复用的代码。在我看来,Controller 里面就只应该存放这些不能复用的代码,这些代码包括:

在初始化时,构造相应的 View 和 Model。

监听 Model 层的事件,将 Model 层的数据传递到 View 层。

监听 View 层的事件,而且将 View 层的事件转发到 Model 层。

若是 Controller 只有以上的这些代码,那么它的逻辑将很是简单,并且也会很是短。

可是,咱们却很难作到这一点,由于仍是有不少逻辑咱们不知道写在哪里,因而就都写到了 Controller 中了,那咱们接下来就看看其它逻辑应该写在哪里。

如何对 ViewController 瘦身?

objc.io 是一个很是有名的 iOS 开发博客,它上面的第一课 《Lighter View Controllers》 上就讲了不少这样的技巧,咱们先总结一下它里面的观点:

UITableView 的 Data Source 分离到另一个类中。

将数据获取和转换的逻辑分别到另一个类中。

将拼装控件的逻辑,分离到另一个类中。

你想明白了吗?其实 MVC 虽然只有三层,可是它并无限制你只能有三层。因此,咱们能够将 Controller 里面过于臃肿的逻辑抽取出来,造成新的可复用模块或架构层次。

我我的对于逻辑的抽取,有如下总结。

将网络请求抽象到单独的类中

新手写代码,直接就在 Controller 里面用 AFNetworking 发一个请求,请求的完数据直接就传递给 View。入门一些的同窗,知道把这些请求代码移到另一个静态类里面。可是我以为还不够,因此我建议将每个网络请求直接封装成类。

把每个网络请求封装成对象实际上是使用了设计模式中的 Command 模式,它有如下好处:

将网络请求与具体的第三方库依赖隔离,方便之后更换底层的网络库。实际上咱们公司的 iOS 客户端最初是基于 ASIHttpRequest 的,咱们只花了两天,就很轻松地切换到了 AFNetworking。

方便在基类中处理公共逻辑,例如猿题库的数据版本号信息就统一在基类中处理。

方便在基类中处理缓存逻辑,以及其它一些公共逻辑。

方便作对象的持久化。sql

你们若是感兴趣,能够看咱们公司开源的 iOS 网络库:YTKNetwork。它在这种思考的指导下,不但将 Controller 中的代码瘦身,并且进一步演化和增强,如今它还支持诸如复杂网络请求管理,断点续传,插件机制,JSON 合法性检查等功能。

这部分代码从 Controller 中剥离出来后,不但简化了 Controller 中的逻辑,也达到了网络层的代码复用的效果。

将界面的拼装抽象到专门的类中

新手写代码,喜欢在 Controller 中把一个个 UILabel ,UIButton,UITextField 往 self.view 上用 addSubView 方法放。我建议你们能够用两种办法把这些代码从 Controller 中剥离。

方法一:构造专门的 UIView 的子类,来负责这些控件的拼装。这是最完全和优雅的方式,不过稍微麻烦一些的是,你须要把这些控件的事件回调先接管,再都一一暴露回 Controller。

方法二:用一个静态的 Util 类,帮助你作 UIView 的拼装工做。这种方式稍微作得不太完全,可是比较简单。

对于一些能复用的 UI 控件,我建议用方法一。若是项目工程比较复杂,我也建议用方法一。若是项目太紧,另外相关项目的代码量也很少,能够尝试方法二。

构造 ViewModel

谁说 MVC 就不能用 ViewModel 的?MVVM 的优势咱们同样能够借鉴。具体作法就是将 ViewController 给 View 传递数据这个过程,抽象成构造 ViewModel 的过程。

这样抽象以后,View 只接受 ViewModel,而 Controller 只须要传递 ViewModel 这么一行代码。而另外构造 ViewModel 的过程,咱们就能够移动到另外的类中了。

在具体实践中,我建议你们专门建立构造 ViewModel 工厂类,参见 工厂模式。另外,也能够专门将数据存取都抽将到一个 Service 层,由这层来提供 ViewModel 的获取。

专门构造存储类

刚刚说到 ViewModel 的构造能够抽奖到一个 Service 层。与此相应的,数据的存储也应该由专门的对象来作。在小猿搜题项目中,咱们由一个叫 UserAgent 的类,专门来处理本地数据的存取。

数据存取放在专门的类中,就能够针对存取作额外的事情了。好比:

对一些热点数据增长缓存

处理数据迁移相关的逻辑

若是要作得更细,能够把存储引擎再抽象出一层。这样你就能够方便地切换存储的底层,例如从 sqlite 切换到 key-value 的存储引擎等。

经过代码的抽取,咱们能够将本来的 MVC 设计模式中的 ViewController 进一步拆分,构造出 网络请求层、ViewModel 层、Service 层、Storage 层等其它类,来配合 Controller 工做,从而使 Controller 更加简单,咱们的 App 更容易维护。

另外,不知道你们注意到没,其实 Controller 层是很是难于测试的,若是咱们可以将 Controller 瘦身,就能够更方便地写 Unit Test 来测试各类与界面的无关的逻辑。移动端自动化测试框架都不太成熟,可是将 Controller 的代码抽取出来,是有助于咱们作测试工做的。编程

MVVM 的历史

MVVM 是 Model-View-ViewModel 的简写。

相对于 MVC 的历史来讲,MVVM 是一个至关新的架构,MVVM 最先于 2005 年被微软的 WPF 和 Silverlight 的架构师 John Gossman 提出,而且应用在微软的软件开发中。当时 MVC 已经被提出了 20 多年了,可见二者出现的年代差异有多大。

MVVM 在使用当中,一般还会利用双向绑定技术,使得 Model 变化时,ViewModel 会自动更新,而 ViewModel 变化时,View 也会自动变化。因此,MVVM 模式有些时候又被称做:model-view-binder 模式。

具体在 iOS 中,可使用 KVO 或 Notification 技术达到这种效果。

在使用中,我发现你们对于 MVVM 以及 MVVM 衍生出来的框架(好比 ReactiveCocoa)有一种「敬畏」感。这种「敬畏」感某种程度上就像对神同样,这主要表如今我没有听到你们对于 MVVM 的任何批评。

我感受缘由首先是 MVVM 并无很大程度上普及,你们对于新技术通常都不熟,进而不敢妄加评论。另外,ReactiveCocoa 自己上手的复杂性,也让不少人感受到这种技术很高深难懂,进而加剧了你们对它的「敬畏」。

MVVM 的做用和问题

MVVM在实际使用中,确实可以使得 Model层和View层解耦,可是若是你须要实现 MVVM中的双向绑定的话,那么一般就须要引入更多复杂的框架来实现了。

对此,MVVM的做者John Gossman 的 批评 应该是最为中肯的。John Gossman 对MVVM的批评主要有两点:

第一点:数据绑定使得 Bug 很难被调试。你看到界面异常了,有多是你 View 的代码有 Bug,也多是 Model 的代码有问题。数据绑定使得一个位置的 Bug 被快速传递到别的位置,要定位原始出问题的地方就变得不那么容易了。

第二点:对于过大的项目,数据绑定须要花费更多的内存。

某种意义上来讲,我认为就是数据绑定使得 MVVM 变得复杂和难用了。可是,这个缺点同时也被不少人认为是优势

咱们应该客观评价 MVVM,可是可是,我忽然想到,我好象只须要一个ViewModel 而已,我彻底能够简单地作一个 ViewModel 的工厂类或 Service 类就能够了,为何要引入这么多框架?现有的 MVC 真的有那么大的问题吗?

有一些人老是追赶着技术,有什么新技术无论三七二十一立马就用,结果被各类坑。

又有一些人,老是担忧新技术带来的技术风险,不肯意学习。结果如今还有人在用 MRC 手动管理引用计数。

而我想说,咱们须要保持的是一个拥抱变化的心,以及理性分析的态度。在新技术的面前,不盲从,也不守旧,一切的决策都应该创建在认真分析的基础上,这样才能应对技术的变化。

相关文章
相关标签/搜索