一千个程序员眼中有一千种 MVC
SmallTalk MVC
Models
- Models 表示知识。
- 一个 Model 可以是单个对象,也可以是一些对象的组合结构。
- Model 和它的部件之间可能有一对一的通信。
- 对 Model 的所有者(View)来说,它如实地反映了真实世界。
- Model 是问题的一个可标示部分。
- 某个 Model 的所有节点都应该处在同样的问题等级,将面向问题的节点(如日历中的预约)和实现细节(如图形)混在一起是不好的。
Views:
- View 是它的 Model 的(可见)表示。
- 它会重点关注 Model 中的某些属性而忽视其它的,因此它也是一个展示过滤器。
- View 和它的 Model(或者 Model 的一部分)连接。它以问问题的方式得到展示所需的数据,发送合适的消息来更新 Model。所有这些问题和消息都在 Model 的术语中,因此 View 必须知道它所展示的 Model 的属性的语义。
Controllers
- Controller 是用户和系统的桥梁。
- 它指定相关的 Views 让它们将自己展示在屏幕的适当位置。
- 它通过菜单或者其它可以支持命令和数据的形式来表示用户的意图。
- Controller 不应该去补充 Views,比如它不会在几个 View 之间绘制箭头把它们连接起来。
- View 永远不会知道用户的输入,譬如鼠标操作和键盘点击。在 Controller 可以写一个方法向 View 发送消息,它可以精确模拟用户的一系列指令。
Editors
- 一个 Controller 和它所有的 Views 相关联,它们被称为 Controller 的部件。
- 某些 Views 提供一种特殊的 Controller——Editor。
- Editor 允许用户修改 View 所展示的信息。
- 这样的 Editor 可能被插入 Controller 和 View 之间,它表现得像 Controller 的一个扩展。
- 一旦编辑操作结束,Editor 被移除。
- Editor 通过它所连接的 View 的 metaphors(隐喻,象征)来和用户交流,因此 Editor 和 View 是强关联的。
- Controller 只能向 View 请求从而得到一个 Editor 的引用,别无他法。
观点
以上是对 MVC 原始论文的整理翻译。
在 MVC 中,V 可以和 C 通信,V 可以和 M 通信。
最早的 MVC 于 1979 年提出,当时还需要程序员全权处理用户输入——Controller 的职责。而现在,大部分事情已经被操作系统做了,我们已经做不到“View 永远不会知道用户的输入”,我们也不太需要 Editor,因为这些已经被封装进UITextField
之类的控件中,View 本身就具备一定的交互功能。用户操作往往被封装成“事件”传递给 View。
所以真正的 MVC 是一种过时的架构。
Apple MVC
Model Objects
Model Objects 包括应用所需的数据,并且定义数据处理的逻辑。
Model 和 View 之间不该有明确的关联。
用户在 View 层的操作会通过 Controller 增加或改变 Model。
Model 一旦改变(譬如从网络获取了新数据),它会通知 Controller,Controller 再去更新 View。
View Objects
View Object 是应用中用户可以看到的部分。
View 知道如何绘制自己,可以响应用户操作。
View 展示 Model 中的数据,并且允许编辑这些数据。尽管如此,View 和 Model 是完全解耦的。
View 通过 Controller 得知 Model 中数据的变化;并且将用户发起的变化通过 Controller 反映到 Model。
Controller Objects
Controller 是 View 和 Model 之间的中间人。
Controller 也可以用来设置和协调应用的任务、管理其它对象的生命周期。
Controller 会将 View 层的变化反映到 Model 层,也会将 Model 层的变化反映到 View 层显示出来。
观点
Apple 所谓的 MVC 跟原始的 MVC 基本已经毫无关系……在这里 C 成了一个中介者,用以协调 V 和 M。
这个模式其实没有特别大的问题,但是由于 Cocoa 中的 ViewController 还承担了 View Container 的工作,我们在日常开发中又容易把 Model 层设计得过于单薄(比如只是一个单纯的数据对象),从而导致 Controller 中有大量本该放在 View 和 Model 中的代码。
所以这是一个最容易被滥用的模式。
MVVM
ViewModel
- 一个抽象的视图(AbstractView)。
- 包含概念:视图状态(ViewState)、数据转换器(ValueConversion)、操作 Model/ViewModel 的指令。
数据绑定
- 需要一种绑定机制将 View 和 ViewModel 连接起来(View 和 ViewModel 可以用不同语言编写)。
观点
MVVM 由微软架构师 John Gossman 于 1996 年提出,在提出这个概念以前,他们团队早已在实践这个架构了。由于 MVVM 是 MVC 的一种改进,M 和 V 部分和 MVC 是类似的。而 Gossman 认为在现代 GUI 系统中,C 的大部分工作已经由系统帮你做了,所以 C 并没有被抛弃,而是隐藏到幕后了。
MVVM 中的 VM 承担了状态管理、数据转换、操作处理之类的任务,它早先被用于 WPF(View 层由 XMAL 编写,且内建了绑定机制),但写 WPF 并不一定要用 MVVM,你完全可以将 View 和 Model 直接绑定——如果你的程序足够简单。
由于在 iOS 中并没有一个内建的绑定机制,很多人觉得在项目中多一层数据转换层就是 MVVM 了,这有一些片面。我还是觉得真的要用 MVVM 就必须建立一套绑定机制,可以利用 RxSwift 和 RAC 之类的第三方库,或者自己撸一套。
MVP
1996年,Mike Potel 在一篇论文中提出 Model-View-Presenter 的概念。这个时候的 View 已经跟 MVC 刚诞生时的 View 全然不同了,它可以接受用户输入。MVP 的主要思想是用户输入由 V 流进,V 通过 P 更新 M,同时 V 跟 M 之间还是跟 MVC 中一样,V 可以调用 M 的接口,M 通过观察者模式向 V 广播自身的更新。
同 MVC 一样,而今的 MVP 与最早的 MVP 也相距甚远。现在为人所普遍接受的 MVP 是,V 和 M 完全解耦,通过 P 进行通讯。但和 Apple MVC 不同的是,P 并不包含生命周期控制之类的职责,而且一般是由 V 去持有 P。
观点
MVP 似乎在 Android 开发中比较流行,我没有实践过,不敢妄言。
VIPER
由 View、Interactor、Presenter、Entity、Router 组成。
观点
同样没有实践过……但直观上感觉是把 MVP 进行了进一步拆分——Presenter 拆为 Presenter 和 Router,Model 拆为 Interactor 和 Entity。
小结
对于结构划分,还是要根据项目规模来,规模大就分层细一点,规模小就粗一点。因为分层越多,层与层之间的通信成本就越高。通信方面可以采取各种手段——接口调用、观察监听、数据绑定等。
我个人比较倾向于分为 View、Model、ViewModel、Router 这几层,以数据绑定为基础进行通信。
各个层最好都定义一个协议来确认各自的职责,可以有一些默认实现。