正确认识 MVC/MVP/MVVM(一 MVC)

作者: android 发布时间: 2021-01-07 浏览: 1521 次 编辑

前言

做移动端开发和前端开发的人员,对 MVC、MVP、MVVM 这几个名词应该都不陌生,这是三个最常用的应用架构模式,目的都是为了将业务和视图的实现代码分离,从而使同一个程序可以使用不同的表现形式。不过,网上的文章对这方面的解说众说纷纭,其中不乏有些错误的描述,导致有些人应用这些架构模式时陷入一些错误陷阱。本文将追根溯源,力求让大伙对这三个架构模式形成正确认识。

MVC = Model-View-Controller,MVP = Model-View-Presenter,MVVM = Model-View-ViewModel。这三个架构模式,都分别有三个不同的部件,都有相同的 Model 层和 View 层。Model 为模型层,主要管理业务模型的数据和行为;View 为展示层,其职责就是管理用户界面。三个架构模式目的都是为了解耦 Model 和 View,主要不同点就在于三者实现解耦的方案不同。Controller、Presenter、ViewModel,对应着三种不同的解耦方案,三种与 M 和 V 的连接方式。

下面,我们先从 MVC 开始,正确理解了 MVC,另外两个就非常容易理解了。

MVC

MVC 最初被用于 Smalltalk-80,对 MVC 最早的解说来源于一篇论文《Applications Programming in Smalltalk-80(TM):How to use Model-View-Controller (MVC)》,那大概是在 1979 年的时候。该论文对 M-V-C 三个模块以及他们之间的通信都阐述了一些设计细节。

在 MVC 中,对应用程序划分出了三种角色:Model、View、Controller。三者有各自的具体用途和职责,并通过彼此的相互通信实现程序功能。对 MVC 了解不深的人其实存在疑惑,比如,Model 是否等于实体类?业务逻辑是归于 Controller 还是 Model?Model 与 View 具体是如何通信的?这些问题你会在下面的内容找到答案。

MVC 三件套中,最难理解的就是 Model,所以我们先来剖析它。前面我们说过,Model 层主要管理业务模型的数据和行为,它既保存程序的数据,也定义了处理该数据的逻辑,所以 Model = 数据 + 业务逻辑。因此,处理业务逻辑属于 Model 的职责,而非 Controller。从数据的维度来说,可以细分为数据的定义、数据的存储、数据的获取。数据的定义其实就是定义数据结构,一般用实体类来定义,以方便在不同角色间传递数据。数据的存储和获取则可能有几种途径:数据库、网络或缓存等。因此,在实际应用中,一个 Model 并不只是简单的一个对象,而是一个更广泛的层级。很多时候,会将 Model 层再进行分解,比如应用于客户端程序时,可以将 Model 层再细分为业务逻辑层、网络层、存储层等,而实体类其实只是贯穿其中的一种数据结构而已。不过,狭义上,当我们说一个 Model 对象的时候,主要是对外部组件而言的,更多是指这个 Model 对外所提供的数据,并不关心数据从何而来。这可能就是让很多人将 Model 误认为就是实体类的原因。

View 是 MVC 里最好理解的,它会接收用户的交互请求并展示数据信息给用户。一个 View 展示的数据可能只是一个 Model 对象的部分数据,也可能是一个 Model 对象的全部数据,甚至可能是多个 Model 对象数据的组合。在 MVC 里,View 被设计为可嵌套的,使用了组合(Composite)模式来实现。比如,列表视图(ListView)或表格视图(TableView)由每个 Item 组成,每个 Item 又可以由图片、文本、按钮等组成。View 是倾向于可复用的,因此,在实际应用中,倾向于将 View 开发成相对通用的组件。

Controller 层主要担任 Model 与 View 之间的桥梁,用于控制程序的流程。Controller 负责确保 View 可以访问到需要显示的 Model 对象数据,并充当 View 了解 Model 更改的渠道。View 接收到用户的交互请求之后,会将请求转发给 Controller,Controller 解析用户的请求之后,就会交给对应的 Model 去处理。因此,理论上,Controller 应该是很轻的。

MVC 通信机制

其实,MVC 发展至今,三件套之间的通信机制,已经演化出多种通信路径。我们先来看看最初的版本:

该版本的 MVC 其实可以看成是由三个基本设计模式组合而成:组合模式、策略模式、观察者模式。组合模式前面已经讲过,就是用在了 View 层。View 与 Controller 之间则用了策略模式,Controller 对象为一个或多个 View 对象实现了策略,View 对象仅限于保持其视觉外观,而与程序逻辑相关的所有决策都委托给 Controller,即 View 可以使用不同的 Controller 实现,得到不同的行为。Model 与 View 之间则使用了观察者模式,View 会注册为 Model 的观察者,当 Model 有变化的时候,就能通知到 View。

所以,一般的交互流程就是:

  1. 用户操纵 View,接着生成一个事件。比如用户点击某个按钮,则会生成一个点击事件。
  2. Controller 对象接收事件并解释该事件,即,它应用了策略。该策略可以是请求 Model 对象以更改其状态,或请求 View 以更改其行为或外观。比如,一个注册按钮产生的事件被 Controller 接收之后,那它就会解释该事件,可能先校验用户的输入是否为空,如果为空则请求 View 提示让用户填写用户名和密码等;如果校验通过,那就请求 UserModel 创建新用户。
  3. 当状态改变时,Model 对象又通知所有已注册为观察者的对象。如果观察者是 View 对象,则可以相应地更新其外观或行为。还是上面的例子,UserModel 创建新用户成功后,就可以通知观察者们,相应的 View 对象接收到 UserModel 创建新用户成功的通知后,就可以跳转到注册成功后的页面了。

这就是 MVC 最初版本的通信机制,自 1979 年提出之后就被广泛应用在 GUI 程序中。请注意,那时候还没有 HTTP。后来随着微软 ASP.NET MVC Framework 的出现,MVC 也开始被广泛应用于 Web 程序。

变种 MVC

以上版本的 MVC,由于 View 依赖了 Model,实际上减低了 View 的可复用性。那么,如果能将 View 和 Model 彻底解耦,那就可以提高 View 的可复用性了。因此,出现了下面这种变种 MVC:

View 和 Model 不直接通信了,而统一通过 Controller 实现数据的传递。Model 将结果告知 Controller,Controller 再去更新 View。

据我所知,苹果提出了这种变种,在苹果之前,有没有其他人提出该变种,我不得而知。另外,很多 Web 框架的设计也是基于这种变种模式的 MVC 的设计思想,比如 SpringMVC 框架,当然,实际的实现比这个复杂得多,但主要设计思路还是 MVC。

这个变种,很多人会将其误认为另外一个经典架构模式「三层架构」,即他们认为 MVC 就是三层架构。其实,两者是不同的。三层架构分别为:表现层、业务逻辑层、数据访问层。虽然和 MVC 的通信方式很相似,但划分的各层的职责是不同的,最重要的是,两者的使用范围不同。三层架构是从整个应用程序架构的角度来划分的三层,而 MVC 只是表现层里再进行功能划分的设计方案,因此,要说两者之间有什么关联,那也是 MVC 属于三层架构里的一个子集。

接着,我们来看看在实际应用中的 MVC 结构又是怎样的?实际应用中,主要还是用在 App 开发上,以 iOS 为例,请看下图:


关键在于 UIViewController,其实,它不只是担任了 MVC 里的 Controller 角色,同时也承担了部分 View 的工作。我们可以将 View 角色按不同功能拆解成几个部分,一是负责界面的布局和渲染展示,二是负责界面的生命周期管理,三是负责界面数据的填充。这三部分功能,二和三其实都是由 UIViewController 负责的,即它不止承担 Controller 的角色,也承担了 View 的生命周期管理及 View 的数据管理。而独立的 View 就只剩下界面展示的功能,在 iOS 中主要就是 xib 和 Storyboard 充当这个角色。
Android 的 Activity 也是一样的,同时担任 Controller 和部分 View 的角色。
另外,在 App 应用里,Controller 从 Model 请求数据时,通常会比较耗时,所以 Model 会异步通知 Controller。

MVC总结

先对 MVC 做一个小总结。MVC 为业务和视图的实现分离提供了开创性的设计思路,让负责业务逻辑的 Model 与负责展示的 View 实现了解耦,从而 Model 的复用性高,多个 View 就可以共享一个 Model,以及,在不修改 Model 的情况下就可以替换 View 的表现形式。
在交互上,早期的 MVC,View 是直接依赖于 Model 的,因此,View 的可复用性其实是受限制的。另外,这种模式其实也不适用于前后端分离的 Web 程序。因此,发展出了变种的 MVC,将 View 和 Model 的直接依赖切断,统一通过 Controller 进行调度,从而提高了 View 的可复用性,以及也可以将 MVC 扩展应用到前后端分离的 Web 程序。
不过,在 App 的实际应用中,又是另一种交互结构。出现了一个 ViewController 角色,不止承担 Controller 的职责,也承接了部分 View 的职责,主要包括对 View 的生命周期管理和数据填充等,而原本的 View 角色就只剩下展示的功能。这种方式的主要优点就是 View 变轻了,可复用性更高了;但缺点也很明显,原本的 Controller 是很轻的,但现在的 ViewController 则是很重的,承担了太多职责。
另外,在很多实际项目中,ViewController 这种模式其实还产生了副作用,当开发人员对 MVC 的理解不深的时候,就会错以为 MVC 的 Controller 就是这样子的,就会错将一些属于 View 和 Model 的代码也移到了 ViewController,导致已经很重的 ViewController 变得更臃肿了。因此,MVC 变成了 Massive View Controller,从而偏离了 MVC 原本的初衷。
为了从根本上解决这个问题,因此,很多 App 项目都改用 MVP 或 MVVM。接下来,我们再来看看 MVP 和 MVVM。