首页
Presentation Patterns : MVC、MVP、MVVM

在之前的一篇文章中介绍了mvc三层架构的区别,主要是因为大部分人对mvc的误用。实际上跟mvc一起对比的应该是mvpmvvm这些表现层(Presentation Layer)的设计模式。它们的目的都是把显示的 UI 跟绑定的状态(data)拆开而已,更多的应用在前端。

MVC

我们今天谈的 MVC 大都是应用在 web 框架里面,实际上是经典 MVC 的 web 变体。它们之间的通信方式如下:

  • View 通过 http 提交数据给服务器的 Controller
  • Controller 要求 Model 改变状态
  • Model 执行某些操作,再通过 http 响应将结果回送给 View,View(浏览器)接受到数据更新界面

最早期的 MVC(也就是 Smalltalk MVC)是运用于 GUI 的,它的流程如下:

Controller 接收用户的操作,并根据写好的代码进行相应的操作 —— 触发 Model 层,或者触发 View 层,抑或是两者都触发,整个过程就是这样的。View 层中的数据是通过监听 Model 层数据变化而自动更新的,与 Controller 层无关。Controller 层触发 View 层时,并不会更新 View 层中的数据。

整个 MVC 的设计中包含了三个设计模式。

  • View层,单独实现了组合模式
  • Model层和View层,实现了观察者模式
  • View层和Controller层,实现了策咯模式

关于 MVC 我们需要知道的是应用于 GUI 的 MVC 与 web 的 MVC 是不一样的。

经典 MVC 模式有以下缺点:

  • 模型需要将状态变化传达给视图
  • 视图拥有模型的完整知识,视图和模型之间没有明确的契约,这意味着视图并不像它应该的那样被动。

MVP

Model View Presenter (MVP) 模式是从 MVC 演变而来的,它试图解决上述问题。最初的 MVP 今天也不再使用,根据 Martin Fowler 的说法,我们使用的是Passive ViewSupervising Controller这两种变体。

Passive View

Passive View这种变体是大多数人讨论的 MVP,它的流程如下:

MVP 框架中,View 层不能再直接访问 Model 层,必须通过 Presenter 层提供的接口,然后 Presenter 层再去访问 Model 层。这种方式巧妙地将视图与模型分开,打破了它们之间的关系。MVP 的核心是通过Presenter来与ViewModel交互。

这种方式存在的缺点是Presenter负责全部的ViewModel的同步工作,所有的消息都需要通过Presenter传递。这样Presenter显得很重,维护起来会比较困难。而且由于没有数据绑定,如果Presenter对视图渲染的需求增多,它不得不过多关注特定的视图,一旦视图需求发生改变,Presenter也需要改动。

Supervising Controller

Supervising Controller这种变体一般讨论的比较少。 它与Passive View最大的不同在于 View会直接与Model打交道,并且与Model进行数据绑定。在有的实现中Presenter的职责还包括就是将Model数据传递给View

Supervising Controller模式强调Controller只处理复杂逻辑和部分与ViewModel之间的同步。Controller中尽可能减少ViewModel的同步工作,将其转交数据绑定来实现,Controller只处理同步机制无法处理的复杂的交互和同步。因此,这种模式中ViewModel需要直接通信,存在依赖关系。

这两种模式的选择一般取决于可测试性与复杂性之间的取舍。Passive View由于视图不负责任何逻辑,所以更易于测试。

MVVM

MVVM乍一看好像是MVPPresenter换成了ViewModel。它与MVP的唯一区别就是把ViewModel的同步逻辑自动化了。以前Presenter负责ViewModel同步不再手动地进行操作,而是交给框架所提供的数据绑定功能进行负责。

对于MVVM来说,双向绑定(data-binding) 是它很核心的概念。目前一些前端框架的实现方式,都是以下几种:

  • 数据劫持 (Vue)
  • 发布-订阅模式 (Knockout、Backbone)
  • 脏值检查 (Angular)

另外就是ViewModel到底是一个什么样的存在,很多人都不太清楚。

首先Model一般特指包含领域逻辑的模型,负责业务实现。View是表示ViewModel状态的交互式 UI,它包含数据绑定、事件和行为,需要知道ViewModel。可以理解为Vue单文件中的<template></template>

ViewModel可以同时和多个Domain Model交互,它并不是一个特定Domain Model为了适配View而构建的外观模式。我们可以认为ViewModel是一个特殊的,不依赖于系统GUI框架的View。此外,需要注意的是。一个ViewModel可以应用于多个View,但是一个View只能拥有一个ViewModel。所有的事件和响应逻辑都需要ViewModel来处理,因此它的View变得很简单。比如在Vue中:一个组件的实例代表一个ViewModel

虽然没有完全遵循 MVVM 模型,但是 Vue 的设计也受到了它的启发。因此在文档中经常会使用 vm (ViewModel 的缩写) 这个变量名表示组件实例。

哪些框架实现了MVVM

这个问题一般在一些技术面试中会问到,比如:is React a MVC or MVVM? 另外还有除了angularJS还有哪些MVVM的框架?等等。其实在 MVVM 的维基百科中把基本上实现了双向绑定的框架都归于 MVVM 了。

关于 React 的 MVVM 讨论,有的人认为它只是VVM或是VC,加上Redux等一些状态库才能完整组成MVVMMVVM architectural pattern for a ReactJS application。其实这种问题深究下去已经没有什么意义了,很多框架对于MVVM的实现都有着自己不同的理解,就像MVC一样。我们只要知道最核心的问题,以及如何最大化去利用这些框架才是最重要的。做一个优秀的工程师比做这种技术考古价值要大得多。


参考

  1. Presentation Patterns : MVC, MVP, PM, MVVM
  2. MVC、MVP、MVVM的区别和联系(精讲版)
  3. Ruby on Rails MVC is not the same as Ember.js MVC
  4. 写给前端看的架构文章(1):MVC VS Flux
  5. Retirement note for Model View Presenter Pattern
  6. What are MVP-Passive View and MVP-Supervising controller
  7. 前端架构 101(五):从 Flux 进化到 Model-View-Presenter
  8. 浅析前端开发中的 MVC/MVP/MVVM 模式