用Model-View-ViewModel构建iOS App(转) 转载自 Model-View-ViewModel for iOS [译]

转载自 Model-View-ViewModel for iOS [译]

若是你已经开发一段时间的iOS应用,你必定据说过Model-View-Controller, 即MVC。MVC是构建iOS app的标准模式。然而,最近我已经愈来愈厌倦MVC的一些缺点。在本文,我将重温一下MVC是什么,详述它的缺点,而且告诉你一个新的方式来架构你的app:Model-View-ViewModel。拿出你的流行语bingo card(宾果卡,一种游戏卡片-译者注),由于咱们即将进行一次范式转变。html

Model-View-Controller

Model-View-Controller是一个用来组织代码的权威范式。Apple甚至是这么说的。在MVC下,全部的对象被归类为一个model,一个view,或一个controller。Model持有数据,View显示与用户交互的界面,而view controller调解model和view之间的交互。react

MVC

在上图中,view将用户交互通知给controller。view controller经过更新model来反应状态的改变。model(一般使用Key-Value-Observation)通知controller来更新他们负责的view。大多数iOS应用程序的代码使用这种方式来组织。ios

模型model的对象一般很是很是的简单。不少时候,他们就是Core Data managed objects,或者避免使用Core Data,就是其余流行的数据模型层。根据Apple的文档,model包括数据和操做数据的业务逻辑。在实践中,model层每每很是薄,无论怎样,model层的业务逻辑被拖入了controller。git

视图view一般是UIKit控件(component,这里根据习惯译为控件)或者编码定义的UIKit控件的集合。进入.xib或者Storyboard会发现一个app、Button、Label都是由这些可视化的和可交互的控件组成。你懂的。View不该该直接引用model,而且仅仅经过IBAction事件引用controller。业务逻辑很明显不纳入view,视图自己没有任何业务。github

还有控制器controller。Controller是app的“胶水代码”:协调模型和视图之间的全部交互。控制器负责管理他们所拥有的视图的视图层次结构,还要响应视图的loading、appearing、disappearing等等,同时每每也会充满咱们不肯暴露的model的模型逻辑以及不肯暴露给视图的业务逻辑。这引出了第一个关于MVC的问题...编程

厚重的View Controller

因为大量的代码被放进view controller,致使他们变的至关臃肿。在iOS中有的view controller里绵延成千上万行代码的事并非前所未见的。这些超重app的突出状况包括:厚重的View Controller很难维护(因为其庞大的规模);包含几十个属性,使他们的状态难以管理;遵循许多协议(protocol),致使协议的响应代码和controller的逻辑代码混淆在一块儿。网络

厚重的view controller很难测试,不论是手动测试或是使用单元测试,由于有太多可能的状态。将代码分解成更小的多个模块一般是件好事。架构

遗失的网络逻辑

苹果使用的MVC的定义是这么说的:全部的对象均可以被归类为一个model,一个view,或是一个controller。就这些。那么把网络代码放哪里?和一个API通讯的代码应该放在哪儿?app

你可能试着把它放在model对象里,可是也会很棘手,由于网络调用应该使用异步,这样若是一个网络请求比持有它的model生命周期更长,事情将变的复杂。显然也不该该把网络代码放在view里,所以只剩下controller了。这一样是个坏主意,由于这加重了厚重View Controller的问题。异步

那么应该放在那里呢?显然MVC的3大组件根本没有适合放这些代码的地方。

较差的可测试性

MVC的另外一个大问题是,它不鼓励开发人员编写单元测试。因为view controller混合了视图处理逻辑和业务逻辑,分离这些成分的单元测试成了一个艰巨的任务。大多数人选择忽略这个任务,那就是不作任何测试。

定义模糊的“Manage”

以前我提到了view controller能够管理试图的层次结构;view controller有一个“view”属性,而且能够经过IBOutlet访问视图的任何子视图。当有不少outlet时这样作不易于扩展,在某种意义上,最好不要使用子视图控制器(child view controller)来帮助管理子视图(subview)。

要点在哪?验证用户输入的业务逻辑应纳入controller仍是model呢?

在这里有多个模糊的标准,彷佛没有人能彻底达成一致。貌似不管如何,view和对应的controller都牢牢的耦合在一块儿,总之,仍是会把它们当成一个组件来对待。

Hey!如今有个点子...

Model-View-ViewModel

在理想的世界里,MVC也许工做的很好。然而,咱们生活在真实的世界。既然咱们已经详细说明了MVC在典型场景中的问题,那让咱们看一看一个可供替换的选择:Model-View-ViewModel。

MVVM来自微软,不过不要坚持反对它。MVVM和MVC很像。它正式规范了视图和控制器紧耦合的性质,并引入新的组件。

MVVM

在MVVM里,view和view controller正式联系在一块儿,咱们把它们视为一个组件。视图view仍然不能直接引用模型model,固然controller也不能。相反,他们引用视图模型view model。

view model是一个放置用户输入验证逻辑,视图显示逻辑,发起网络请求和其余各类各样的代码的极好的地方。有一件事情不该纳入view model,那就是任何视图自己的引用。view model的概念同时适用于于iOS和OS X。(换句话说,不要在view model中使用 #import UIKit.h)

因为展现逻辑(presentation logic)放在了view model中(好比model的值映射到一个格式化的字符串),视图控制器自己就会再也不臃肿。当你开始使用MVVM的最好方式是,能够先将一小部分逻辑放入视图模型,而后当你逐渐习惯于使用这个范式的时候再迁移更多的逻辑到视图模型中。

使用MVVM的iOS app是高度可测试的;由于view model包含了全部的展现逻辑而且不会引用view,因此它能够经过编程方式充分测试。虽然有众多的hack技术参与到测试Core Data模型,但使用MVVM写的app能够进行充分的单元测试。

以个人经验,使用MVVM会轻微的增长代码量,但整体上减小了代码的复杂性。这是一个划算的交易。

回过头再来看MVVM的图示,你会注意到我使用了模糊的动词“notify”和“update”,而没有详细说明该怎么作。你可使用KVO,就像MVC那样,但这很快就会变得难以管理。事实上,使用ReactiveCocoa会是更好的方式来组织各个部分。

关于怎么结合ReactiveCocoa来使用MVVM的信息,能够阅读Colin Wheeler的excellent write-up或者看看我写的开源app。你也能够阅读个人关于ReactiveCocoa和MVVM的书.

相关文章
相关标签/搜索