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

Model-View-Controller

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

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

模型model的对象通常非常非常的简单。很多时候,他们就是Core Data managed objects,或者避免使用Core Data,就是其他流行的数据模型层。根据Apple的文档,model包括数据和操作数据的业务逻辑。在实践中,model层往往非常薄,不管怎样,model层的业务逻辑被拖入了controller。

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

还有控制器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通信的代码应该放在哪儿?

你可能试着把它放在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里,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的书.

原文链接

Model-View-ViewModel for iOS by Ash Furrow

延伸阅读

Model-View-ViewModel for iOS [译]的更多相关文章

  1. Qt:Drag-Drop操作在QGraphicsView及Model/View框架下的实现

    最近使用到Qt的Drag Drop功能,结合自己的例子写下来给大家分享一下.实现从QTreeView拖动Node到QGraphicsView上,以及QGraphicsView上item之间的拖动. 先 ...

  2. Qt Model/View(官方翻译,图文并茂)

    http://doc.trolltech.com/main-snapshot/model-view-programming.html 介绍 Qt 4推出了一组新的item view类,它们使用mode ...

  3. MVC模式(Model View Controller)下实现数据库的连接,对数据的删,查操作

    MVC模式(Model View Controller): Model:DAO模型 View:JSP  在页面上填写java代码实现显示 Controller:Servlet 重定向和请求的转发: 若 ...

  4. (转)Qt Model/View 学习笔记 (七)——Delegate类

    Qt Model/View 学习笔记 (七) Delegate  类 概念 与MVC模式不同,model/view结构没有用于与用户交互的完全独立的组件.一般来讲, view负责把数据展示 给用户,也 ...

  5. (转)Qt Model/View 学习笔记 (六)——在views中选择数据项

    在views中选择数据项 概念 用于新的view类中的选择模型比Qt3中的模型有了很大的改进.它为基于model/view架构的选择提供了更为全面的描述.尽管对提供了的views来说,负责操纵选择的标 ...

  6. (转)Qt Model/View 学习笔记 (五)——View 类

    Qt Model/View 学习笔记 (五) View 类 概念 在model/view架构中,view从model中获得数据项然后显示给用户.数据显示的方式不必与model提供的表示方式相同,可以与 ...

  7. (转)Qt Model/View 学习笔记 (四)——创建新的Models

    创建新的Models 介绍 model/view组件之间功能的分离,允许创建model利用现成的views.这也可以使用标准的功能 图形用户接口组件像QListView,QTableView和QTre ...

  8. (转)Qt Model/View 学习笔记 (三)——Model类

    Model类 基本概念 在model/view构架中,model为view和delegates使用数据提供了标准接口.在Qt中,标准接口QAbstractItemModel类中被定义.不管数据在底层以 ...

  9. (转)Qt Model/View 学习笔记 (二)——Qt Model/View模式举例

    Qt Model/View模式举例 Qt提供了两个标准的models:QStandardItemModel和QDirModel.QStandardItemModel是一个多用途的model,可用于表示 ...

随机推荐

  1. 【Swift】TTTAttributedLabel使用小记

    前言 TTTAttributedLabel继承自UILabel,很方便基于现有代码进行修改,Star超过4K+,今天用了一下作点笔记. 声明  欢迎转载,但请保留文章原始出处:)  博客园:http: ...

  2. android性能优化练习:过度绘制

    练习:https://github.com/zhangbz/AndroidUIPorblems 查看过度绘制 在开发者选项中开启"调试GPU过度绘制" 判断标准 无色:没有过度绘制 ...

  3. Linux - expect自动化远程登录脚本

    简单模式: #!/usr/bin/expect -f spawn ssh root@192.168.0.1 expect "*assword*" send "root\r ...

  4. iframe大小自适应

    前几天,舍友去某互联网公司面前端研发工程师.回来后,他就跟我们聊了下面试官给他出的题.其中,有一道题是“如何实现iframe高度的自适应?”.好吧,我承认,我听到iframe这个词的第一反应就是:这个 ...

  5. PS技巧:如何优雅的抠公章?

    搞设计的很苦逼,整天面对各种各样任务,除了修图.排版外,还时不时会有些另类需求.这时如果掌握一些小技巧就不用临时抱佛脚啦. 下面献上一计:教大家怎么用PS抠公章.有需要的拿去,PS:不要干坏事吆! 效 ...

  6. ORA-12516:TNS:listener could not find available handler with matching protocol stack

    应用程序连接测试数据库时报ORA-12516:TNS:listener could not find available handler with matching protocol stack 检查 ...

  7. mybatis配置-返回date类型丢失时间

    此博客仅作于平时开发所遇到的问题记录,不做他用,描述可能不好,自己看懂即可~~ resultMap配置返回时间类型时,发现数据库时间是精确到秒的,但是返回给javabean之后丢失时分秒的信息,只有日 ...

  8. WPF 自定义绕圈进度条

    在设计界面时,有时会遇到进度条,本次讲解如何设计自定义的绕圈进度条,直接上代码: 1.控件界面 <UserControl x:Class="ProgressBarControl&quo ...

  9. EF6 DataMigration 从入门到进阶

    引言 在EntityFramework的开发过程中我们有时因需求变化或者数据结构设计的变化经常会改动表结构.但数据库Schema发生变化时EF会要求我们做DataMigration 和UpdateDa ...

  10. 与JSP的初次邂逅……

    JSP是可以内嵌在网页中,由服务器端来执行与解释的程序,是一种动态网页技术标准. 在传统的HTML文件(*.htm或*.html)中加入Java程序片段和JSP标记,就构成了JSP网页(*.jsp). ...