MVC、MVP和MVVM以及MVA比较


模型-视图-控制器(MVC)模式好像是无处不在,Web框架使用它,GUI客户端使用它,但可能这只是一个营销谎言(如果你想仔细分辨他们的区别,如果你认真,你就可能错了)。他们实际是在使用像MVA这样的东西。

MVC的历史
MVC是在70年代发明的,当时Alan Kay和他的团队在Xerox Parc的Dynabook和Smalltalk工作, Trygve MH Reenskaug记得
我创建了模型 - 视图 - 控制器模式,作为从多个角度看待用户控制其信息的一般问题的明显解决方案。MVC创造了令人惊讶的兴趣。有些文本甚至使用变态变体实现计算机控制用户的相反目的。

MVC不是1994年Gang of Four四人帮《设计模式》一书中的一部分。我认为Smalltalk实现是MVC参考,因为它是多个项目和开发人员使用的第一个实例。
2003年,Reenskaug重新审视了他的设计,并将其分为多种模式。他称之为MVC模式语言

  • “模型/编辑器分离”描述了模型与接口的分离以与之交互。这使得模型能够接近用户的心理模型,这被认为是良好的面向对象设计。模型中不需要考虑接口。
  • “输入/输出分离”是关于通过控制器处理用户输入并通过视图将模型呈现给用户的分割。这个基本思想归功于Smalltalk-80实现。
  • “任务工具”模式是“原始MVC”。它解决的问题是“为用户提供执行一项或多项任务的工具。该工具应该让用户产生与模型直接交互的错觉。”

从GoF设计模式术语表达:控制器和视图部件是模型的外观facade模式

来自架构原则的MVC
我们还可以从架构原则中推导出MVC,并且由于我们可以简单地分两步描述设计模式,这两个步骤直接映射到Reenskaug的“模型/编辑器分离”和“输入/输出分离”。

  1. 在分层架构中,我们希望将UI与较低级别的模型分离。较低的水平一定不能知道更高的水平。他们使用抽象来进行更松散的耦合。因此,我们必须使用观察者从模型调用UI。
  2. 我们还希望将用户交互与图形表示分离以增加内聚力,因此我们在UI层上拆分为控制器和视图。由于它们都必须直接引用模型,因此它们保持在同一水平,并且可以自由地进行交互而无需观察者解耦。

这种推导是我将模型的绘制必须低于控制器和视图的原因。Smalltalk-80的描述似乎有相同的想法。
第二步在当今的环境中没有很好的动力。通常不需要实际拆分控制器和视图。John Gossmann 在2005年评论道
Controller在现代GUI开发中究竟发生了什么,这是一个漫长的题外话......我倾向于认为它刚刚陷入了背景。它仍然存在,但我们不必像1979年那样考虑它

但是,作为一个框架创建者,您如果声明是MVC,就会给人一种熟悉的感觉。在这种情况下,只需在UI层中选择两个组件,即可调用一个视图,再调用另一个视图。

MVC的变化
Model-View-Presenter(MVP)是一个MVC变体。Greer绘制的类图与他的MVC类图完全相同,只是他将“Controller”改为“Presenter”。这仅暗示实际差异:Controller控制器具有处理输入的主要工作,并可相应地修改模型。Presenter演示者具有修改模型的主要工作,而视图也处理输入。

Model-View-ViewModel(MVVM)还将输入处理合并到视图中,它完全解耦模型和视图。在MVC中,视图直接连接到模型,而反向是通过观察者抽象的。MVVM将视图与模型断开连接,并将ViewModel作为中间体间接地引入。例如,ViewModel中常见的是选择逻辑,它通常会同时影响多个GUI元素。
MVVM的主要动机是使视图能够在没有任何逻辑的情况下由XML定义,因此没有编程技能的UI设计人员可以构建它。
使用MVVM的主要场景不是Smalltalk,而是Microsofts WPF应用程序。
MVVM也是Fowlers Passive View 理念的一个实例, 其动机是改进的可测试性:GUI不能很好地进行单元测试,因此将GUI部分缩小并尽可能多地放入其他代码。

模型 - 视图 - 适配器(MVA)
MVA完全解耦模型和视图,但不打算在视图中没有逻辑,输入也来自视图,因此这将控制器变为纯粹的适配器。当需要将数据库架构与GUI分离时,适配器很有价值。
由于MVA和MVVM的类图是同构的,我认为MVVM是WPF特定的MVA。

结论
MVC不是一个定义明确的概念,所以不要认为它是上述模式中任何一个。把它当成像“云”这样的流行语会更好。MVC在特定的上下文中可能是精确的(例如Android应用程序开发),但通常只是一个模糊的概念。

如果您设计一个系统,那么考虑MVC与MVVM和MVP之类的选项是没有帮助的。相反,找出潜在的问题,并在必要时解决它们。例如,UI和模型之间的耦合可能是一个问题,因为它会使其中任何一个更改成本更高,因为您还必须调整其他部分。您可以使用观察者而不是直接调用来主动解耦它们。你也可以使用观察者以外的东西,或者根本不使用它。