本文从细节入手,尝试分析了几种常见的 GUI 架构:MVC、MVCS、MVP、MVVM。对于在实际开发中如何选择给出了一些参考意见。html
本文同时发表于个人我的博客react
移动开发架构,不管是 iOS、Andriod 仍是 Web 都属于 GUI (Graphical User Interfaces) 架构范畴。2006年,Martin Fowler 的 GUI Architectures 一文可谓是经典之做。文中 Martin Fowler 提到 MVC 模式如何组织代码、划分模块职责,还提到 Data Binding、Flow Synchronization 以及 Observer Synchronization 等核心概念。 纵观十年来 GUI 架构演变,不管是 MVCS、MVP 仍是 MVVM,其实讨论的核心问题仍是如何分层、如何划分模块职责、作好代码隔离。ios
谈到 iOS 上常见架构,相信只要有半年以上开发经验的同窗都能侃侃而谈。但在实际交流过程当中发现很多同窗对关键细节问题却认知模糊,甚至是错误的。所以,本文尝试从细节入手对几种常见架构进行简单描述(对架构的认识智者见智、仁者见仁,我所描述的也不必定是正确的)。git
上文提到各类架构虽各类不一样,但它们其实都是在讨论一个问题:『如何分层』。 那么在继续以前,咱们有必要思考一下:为何要分层?github
计算机界有一句大道至简的名言:设计模式
All problems in computer science can be solved by another level of indirection.安全
之因此要分层,最终目的是下降系统总体复杂度。 经过分层咱们至少能得到如下能力:网络
说到分层,可能最早想到的例子是 OSI 七层或 TCP/IP 五层网络模型: 架构
现在对网络安全愈来愈重视,HTTPS 协议也慢慢普及,经过分层,只需在原有 HTTP 协议基础上添加一个 TLS/SSL 的安全传输层便可,由它来负责加解密,而原有的 HTTP、TCP 协议无需修改: app
MVC(Model-View-Controller)做为最经典的架构,广为人熟知,也是 Apple 官方推荐的移动架构。
Separated Presentation: Ensure that any code that manipulates presentation only manipulates presentation, pushing all domain and data source logic into clearly separated areas of the program.
Observer Synchronization: Synchronize multiple screens by having them all be observers to a shared area of domain data.
经常使用方法:在Presentation Object(Controller)中注册通知、设置delegate、传递block等。当数据须要更新时,Domain Object(Model)经过上述方式将数据自底向上的同步给Presentation Object。
下面简单介绍一下 Model、View、Controller:
Apple: Model Objects Encapsulate Data and Basic Behaviors. Stanford: Model = What your application is (but not how it is displayed). 简单讲:Model = Data + Manipulate Data
(ps:本文中的Stanford表示斯坦福的 iOS 公开课)
QRBookShelfModel
,包含了数据:
NSArray<QRBookShelfItem *> *books
以及对数据的操做:
addBook:
、
deleteBook:
等。
Apple: Controller Objects Tie the Model to the View. Stanford: Controller = How your Model is presented to the user(UI logic).
Controller 是 Model 与 View 间的链接器,其核心职责有:
这里有个问题:到底什么是展现逻辑? 简单讲:将业务数据转换成UI数据,如:
Apple: View Objects Present Information to the User Stanford: View=Your Controller’s minions
总之,View 只作一件事:layout。
为了实现 MVC 的核心思想:业务 (model) 与展现 (View) 的隔离,必须严格遵照一些规则:
看到这里,你们有没有一种熟悉的味道? 没错,UITableView 与外界 (Contoller) 的交互与此处的描述高度一致。
此时,你们或许心中有些疑问: 一、在 MVC 模式中,网络请求、数据存储谁来完成? 二、Model、View、Controller 谁的可复用性最强? 三、展现逻辑为何由 Controller 完成而不是 View?
在 MVC 模式中,网络请求、数据存储谁来完成? Controller、Model 均可以,通常由 Model 完成。此时的 Model 已再也不是简单的 Model Object,而是 Model layer,在咱们项目中一般将其称为 Manager。
Model、View、Controller 谁的可复用性最强? View>Model>Controller
展现逻辑为何由 Controller 完成而不是 View? View可复用性高,不该关心具体展现逻辑,只专一于 layout
MVC 模式被批评最多的就是 Controller 过于臃肿,那么 Controller 都作了什么?
尤为是现在不少产品经理『擅长』作加法,页面、交互愈来愈复杂,这对于 Controller 来讲无疑是雪上加霜。
前面提到,在 MVC 模式中,并无讨论获取数据属于哪一个模块的职责 (通常由 model 负责)。MVCS 模式就是在 MVC 基础上将数据单独提取为一层(Store)。
在 MVC 模式中,展现逻辑被划分为 Controller 的职责范围。现在,展现逻辑愈来愈复杂,Controller 随之也变得愈来愈臃肿。同时,Controller 也被认为是 View 的一部分,这样 Model 与 View 间并无彻底隔离、解耦。 MVP (Model-View-Presenter) 就是在这样的背景下产生的,其将展现逻辑提取为一个单独的层(Presenter),简化了 Controller,也完全隔离了 Model 与 View。
最近两三年对 MVVM(Model View View-Model) 的讨论比较多,其提出的愿景也是为了简化 Controller、完全将 View 与 Model 解耦、并提供 Data Binding。
在 MVVM 模式中,各模块间的依赖关系、数据流向、数据传递的格式都有严格的规定:
View(UIViewController/UIView): 1.能够依赖(持有) View、View Model,便可直接调用其方法; 2.不能依赖(持有) Model、Model Object(Item); 3.UI 绑定到View Model上(如:titleLabel.text->viewModel.title) 4.经过 RACCommands/RACActions 或直接调用 View Model 的方法将用户事件传递给 View Model。
View Model: 1.能够依赖(持有) View Model、Model 以及 Model Object; 2.不能依赖(持有) View、Raw Model Object; 3.其公开属性只能是基础数据类型(NSInteger、NSString等)或其余 View Model; 4.将 Model Object 转换成可直接在 View 上显示的属性或Sub View Model(展现逻辑); 5.接受来自 View 或 Sub View Model 的输入(用户事件)。
Model (Layer): 1.能够依赖(持有) 其余 Model、Model Object、Data Source、Raw Model Object; 2.不能依赖(持有) View Model、View; 3.将 Raw Model Object 转换为 Model Object; 4.为 View Model 提供数据(异步)。
其中,View 与 View Model 相似 UIView 与 CALayer 的关系,一一对应(包括层次结构):
从 Data Source 到 Model、Model 到 View Model 可采用通常的同步方法,如:Delegate、Notification 以及 block 等。而从 ViewModel 到 View 的 Data Binding 是 MVVM 模式与其余 MV* 模式最大的区别。
遗憾的是 iOS 并无原生的 Data Binding 方式,目前大概只能经过两种方式实现 Data Binding:KVO 或 ReactiveCocoa。KVO/RAC是一种更加激进的 Observer Synchronization:
MVVM 与 MVP 有不少类似的点:
二者最大的区别在于:MVVM 有 Data Binding 而 MVP 没有。
根据是否有 Data Binding,可将常见 GUI 构架分为两大阵营:
MVVM 的 Data Binding 在必定程度上增长了编码的复杂度、数据流也变得不够直观、调试难度也有所增长。但对于数据可变的场景,一旦经过 Data Binding 将 View 与 View Model 绑定起来,在数据变化时,会自动映射到 UI 上,十分方便。
根据展现逻辑是否独立于 Controller,可分为:
MVVM、MVP 分别将展现逻辑从 Controller 中提取出来,使 Controller 获得必定程度的简化,在展现逻辑复杂的状况下,效果更加明显。
经过上述分析,咱们能够看到,常见几种架构:MVC、MVCS、MVP、MVVM 并无绝对的好坏之分,只是各有不一样的适用场景。 咱们在选择时能够根据如下两点做为参考依据:
MVCS、MVP、MVVM 等各类新生架构,虽各有不一样,但都是源自于 MVC,它们的核心思想一直没变,也不能变:
理论的东西讲了很多,下面结合实际的项目,看看应该如何选择架构。
书籍详情页在整个 QQ 阅读 app 中,不管是展现仍是业务逻辑都是最复杂的一个模块。
所以,该模块采用 MVVM 架构比较合适。遗憾的是,当时设计该模块时没有充分意识到其复杂程序,而是选择了传统的 MVC 架构。结果形成详情页 Controller 十分复杂,下面这段就是 Controller 中根据下载状态修改 toolbar 上3个按钮状态的代码(ps:看不清不要紧,只要能看出其很复杂便可^_^):
信息流做为 QQ 阅读一大亮点,能为用户个性化推荐书籍,是整个 app 中最重要的一个页面:
所以,信息流模块不必使用复杂的 MVVM 架构。
不少场景属于此类情形:经过 UITableView 列举多行静态数据,若数据有更新时直接 reload tableview。
各类分层架构都是前辈充满智慧的宝贵经验,值得尊敬、借鉴、学习,但也没必要拘泥于形式,重点是理解其背后的思想。设计模式有六大原则:
其中除了里氏代换原则,其余五大原则都是分层架构的指导思想。只要咱们深入理解并能严格遵照这些原则,不管咱们选择哪一种架构、或在其基础上进行衍化,都能设计出高质量的代码。
代码设计、架构选择及理解仁者见仁、智者见智,但经典的设计理念是公认的、也是通过时间检验的。