混乱的MVC架构
关于MVC,唯一明确的是分为Model、 View 和Controller三个部分,模型层可以单独工作。至于他们之间的耦合,不同的平台有不同的解释。
ASP.NET MVC
我们可以简单的理解为控制器负责管理视图和模型;视图负责展示模型中的内容。
Spring MVC
更加复杂
通过了DispatchServlet将视图层和控制器层完全解耦
iOS MVC
iOS 客户端中的 Cocoa Touch 自古以来就遵循 MVC 架构模式,不过 Cocoa Touch 中的 MVC 与 ASP.NET 和 Spring 中的 MVC 截然不同。
在iOS中,由于UIVIewController类持有一个根视图UIVIew,所以视图层和控制器层紧密耦合。这也是iOS视图控制器非常臃肿重要原因之一。
Rails MVC
视图层和模型层没有直接耦合,而是通过控制器的作为中间人对信息进行传递。
这种MVC的设计,分离了视图层和模型层的耦合,作为承担数据存储功能的模型层,可以通过控制器同时为不同的视图提供数据。
控制器根据用户发出的HTTP请求,从模型中取出相同的数据,然后传递给不同的视图渲染出不同的结果。
标准的MVC
没有一个确切的答案,多个框架到MVC的理解有一点完全相同,就是讲整个应用分为Model、View 和 Controller 三个部分。
作者理解的标准的MVC模型和ASP.NET 中对于 MVC的设计相同:
- 控制器负责对模型中的数据进行更新,而视图向模型中请求数据
- 当用户的行为触发操作的时候,会有控制器更新模型,并通知视图进行更新,在这时视图向模型请求新的数据
依赖关系
模型层可以单独工作,而控制器和模型层都依赖模型层中的数据。
分离展示层
MVC最重要的概念就是分离展示层(Separated Presentation),如何在领域对象(Domain Object)和我们在屏幕上看到的GUI元素进行划分是MVC架构模式最核心的问题。
GUI 应用程序由于其需要展示内容的特点,分为两个部分:一部分是用于展示内容的展示层(Presentation Layer),另一部分包含领域和数据逻辑的领域层(Domain Layer)。
展示层依赖于领域层中存储的数据,而领域层对于展示层一无所知,领域层其实也是 MVC 模式中的模型层,而展示层可以理解为 VC 部分。
MVC 最重要的目的并不是规定各个模块应该如何交互和联系,而是将原有的混乱的应用程序划分出合理的层级,把一团混乱的代码,按照展示层和领域层分成两个部分;在这时,领域层中的领域对象由于其自身特点不需要对展示层有任何了解,可以同时为不同的展示层工作。
观察者同步
在 MVC 模式中,模型可以单独工作,同时它对使用模型中数据的视图和控制器一无所知,为了保持模型的独立性,我们需要一种机制,当模型发生改变时,能够同时更新多个视图和控制器的内容;在这时,就需要以观察者同步的方式解决这个问题。
我们将所有需要实时更新的组件注册成为模型的观察者,在模型的属性发生变化时,通过观察者模式推送给所有注册的观察者(视图和控制器)
当多个视图共享相同的数据时,观察者同步是一个非常关键的模式,它能够在对这些视图不知情的前提下,同时通知多个视图;通过观察者模式,我们可以非常容易地创建一个依赖于同一模型的视图。
占主导地位的控制器
MVC 架构模式的三个组成部分:Model、View 和 Controller 中最重要的就是控制器,它承担了整个架构中的大部分业务逻辑,同时在用户请求到达或者事件发生时都会首先通知控制器并由它来决定如何响应这次请求或者事件。
在 MVC 中,所有的用户请求都会首先交给控制器,再由控制器来决定如何响应用户的输入,无论是更新模型中的信息还是渲染相应的视图,都是通过控制器来决定的;也就是说,在 MVC 中,控制器占据主导地位,它决定用户的输入是如何被处理的。
被动的模型
在绝大多数的 MVC 架构模式中,模型都不会主动向视图或者控制器推送消息;模型都是被动的,它只存储整个应用中的数据,而信息的获取和更新都是由控制器来驱动的。
但是当模型中的数据发生变化时,却需要通过一些方式通知对应的视图进行更新,在这种情况下其实也不需要模型主动将数据变化的消息推送给视图;因为所有对于模型层的改变都是由用户的操作导致的,而用户的操作都是通过控制器来处理的,所以只需要在控制器改变模型时,将更新的信息发送给视图就可以了;当然,我们也可以通过观察者模式向未知的观察者发送通知,以保证状态在不同模块之间能够保持同步。
MVP
MVC 与 MVP 之间的区别其实并不明显,作者认为两者之间最大的区别就是 MVP 中使用 Presenter 对视图和模型进行了解耦,它们彼此都对对方一无所知,沟通都通过 Presenter 进行。
在 MVP 中,Presenter 可以理解为松散的控制器,其中包含了视图的 UI 业务逻辑,所有从视图发出的事件,都会通过代理给 Presenter 进行处理;同时,Presenter 也通过视图暴露的接口与其进行通信。
目前常见的 MVP 架构模式其实都是它的变种:Passive View 和 Supervising Controller,接下来的内容也是围绕这两种变种进行展开的。
被动视图Passive View
MVP 的第一个主要变种就是被动视图(Passive View);顾名思义,在该变种的架构模式中,视图层是被动的,它本身不会改变自己的任何的状态,所有的状态都是通过 Presenter 来间接改变的。
被动的视图层就像前端中的 HTML 和 CSS 代码,只负责展示视图的结构和内容,本身不具有任何的逻辑.
视图成为了完全被动的并且不再根据模型来更新视图本身的内容,也就是说,不同于 MVC 中的依赖关系;在被动视图中,视图层对于模型层没有任何的依赖:
通信方式
- 当视图接收到来自用户的事件时,会将事件转交给 Presenter 进行处理;
- 被动的视图向外界暴露接口,当需要更新视图时 Presenter 通过视图暴露的接口更新视图的内容;
- Presenter 负责对模型进行操作和更新,在需要时取出其中存储的信息;
- 当模型层改变时,可以将改变的信息发送给观察者 Presenter;
在 MVP 的变种被动视图中,模型的操作以及视图的更新都仅通过 Presenter 作为中间人进行。
监督控制器Supervising Controller
与被动视图中状态同步都需要显式的操作不同,监督控制器(Supervising Controller)就将部分需要显式同步的操作变成了隐式的:
在监督控制器中,视图层接管了一部分视图逻辑,主要内容就是同步简单的视图和模型的状态;而监督控制器就需要负责响应用户的输入以及一部分更加复杂的视图、模型状态同步工作。
对于用户输入的处理,监督控制器的做法与标准 MVP 中的 Presenter 完全相同;但是对于视图、模型的同步工作,监督控制器会尽可能地将所有简单的属性以数据绑定的形式声明在视图层中,类似于 Vue 中双向绑定的简化版本
通信方式
监督控制器中的视图和模型层之间增加了两者之间的耦合,也就增加了整个架构的复杂性:
视图和监督控制器、模型与监督控制器的关系与被动视图中两者与 Presenter 的关系几乎相同,视图和模型之间新增的依赖就是数据绑定的产物;视图通过声明式的语法与模型中的简单属性进行绑定,当模型发生改变时,会通知其观察者视图作出相应的更新。
通过这种方式能够减轻监督控制器的负担,减少其中简单的代码,将一部分逻辑交由视图进行处理;这样也就导致了视图同时可以被 Presenter 和数据绑定两种方式更新,相比于被动视图,监督控制器的方式也降低了视图的可测试性和封装性。
占主导地位的视图
MVP 架构模式中,视图层在整个架构中都是占主导地位的:
MVC 与MVP的区别:
- 在 MVC 中,控制器负责以不同的视图响应客户端请求的不同动作;然而,不同于 MVC 模式,MVP 中视图将所有的动作交给 Presenter 进行处理;
- MVC 中的所有的动作都对应着一个控制器的方法调用,Web 应用中的每一个动作都是对某一个 URL 进行的操作,控制器根据访问的路由和方法(GET 等)对数据进行操作,最终选择正确的视图进行返回。
- MVC 中控制器返回的视图没有直接绑定到模型上,它仅仅被控制器渲染并且是完全无状态的,其中不包含任何的逻辑,但是 MVP 中的视图必须要将对应的事件代理给 Presenter 执行,否则事件就无法被响应。
- 另一个 MVP 与 MVC 之间的重大区别就是,MVP(Passive View)中的视图和模型是完全解耦的,它们对于对方的存在完全不知情。
MVVM
MVVM的演变 从展示模型到视图模型
早在 2004 年,Martin Fowler 发表了一篇名为 Presentation Model (以下简称为 PM 模式)的文章,PM 模式与 MVP 比较相似,它从视图层中分离了行为和状态;PM 模式中创建了一个视图的抽象,叫做 Presentation Model,而视图也成为了这个模型的『渲染』结果。
2005 年,John Gossman 在他的博客上公布了 Introduction to Model/View/ViewModel pattern for building WPF apps 一文。MVVM 与 Martin Fowler 所说的 PM 模式其实是完全相同的,Fowler 提出的 PM 模式是一种与平台无关的创建视图抽象的方法,而 Gossman 的 MVVM 是专门用于 WPF 框架来简化用户界面的创建的模式;我们可以认为 MVVM 是在 WPF 平台上对于 PM 模式的实现。
Presentation Model
PM 模式将视图中的全部状态和行为放到一个单独的展示模型中,协调领域对象(模型)并且为视图层提供一个接口。
在监督控制器中,视图层与模型层中的一些简单属性进行绑定,在模型属性变化时直接更新视图,而 PM 通过引入展示模型将模型层中的数据与复杂的业务逻辑封装成属性与简单的数据同时暴露给视图,让视图和展示模型中的属性进行同步。
展示模型中包含所有的视图渲染需要的动态信息,包括视图的内容(text、color)、组件是否启用(enable),除此之外还会将一些方法暴露给视图用于某些事件的响应。
在 PM 模式中,同一个展示模型可以与多个领域对象交互,多个视图可以使用相同的展示模型,但是每一个视图只能持有一个展示模型。
MVVM就是对PM模式的一种实现
WPF 作为微软用于处理 GUI 软件的框架,提供了一套非常优雅的解决方案。
从 Model-View-ViewModel 这个名字来看,它由三个部分组成,也就是 Model、View 和 ViewModel;其中视图模型(ViewModel)其实就是 PM 模式中的展示模型,在 MVVM 中叫做视图模型。
除了我们非常熟悉的 Model、View 和 ViewModel 这三个部分,在 MVVM 的实现中,还引入了隐式的一个 Binder 层,而声明式的数据和命令的绑定在 MVVM 模式中就是通过它完成的。
在iOS上,绑定机制可以使用如下两个方案:
- 基于KVO的绑定库如 RZDataBinding 和 SwiftBond
- 完全的函数响应式编程,比如像ReactiveCocoa、RxSwift或者 PromiseKit
无论是 MVVM 还是 Presentation Model,其中最重要的不是如何同步视图和展示模型/视图模型之间的状态,是使用观察者模式、双向绑定还是其它的机制都不是整个模式中最重要的部分,最为关键的是展示模型/视图模型创建了一个视图的抽象,将视图中的状态和行为抽离出一个新的抽象,这才是 MVVM 和 PM 中需要注意的。
从 MVC 架构模式到 MVVM,从分离展示层到展示模型层,经过几十年的发展和演变,MVC 架构模式出现了各种各样的变种,并在不同的平台上有着自己的实现。
参考链接: