MVC和MVVM有什么区别?

标准的“Model View Controller”模式和Microsoft的Model/View/ViewModel模式之间有区别吗?

654207 次浏览

一方面,MVVM是MVC模式的发展,它使用XAML来处理显示。这篇文章概述了两者的一些方面。

Model/View/ViewModel架构的主旨似乎是在数据(“模型”)之上,有另一层非可视化组件(“ViewModel”),它们将数据的概念更紧密地映射到数据视图(“视图”)的概念。视图绑定到的是ViewModel,而不是模型。

MVVM模型-视图视图模型类似于MVC,模型-视图控制器

控制器被替换为视图模型。ViewModel位于UI层下方。ViewModel公开视图需要的数据和命令对象。您可以将其视为视图从中获取数据和操作的容器对象。ViewModel从模型中提取数据。

Russel East做了一个更详细讨论的博客为什么MVVM不同于MVC

MVVM将视图模型添加到组合中。这很重要,因为它允许您使用WPF的大量绑定方法,而无需将所有UI特定部分放在常规模型中。

我可能错了,但我不确定MVVM是否真的强制控制器加入其中。我发现这个概念更符合:http://martinfowler.com/eaaDev/PresentationModel.html。我认为人们选择将其与MVC结合起来,而不是将其内置在模式中。

MVVM是演示模型模式的改进(值得商榷)。我说值得商榷,因为唯一的区别在于WPF如何提供数据绑定和命令处理的能力。

视图模型是用户交互界面元素的“抽象”模型。它必须允许您以非可视化的方式执行视图中的命令和操作(例如测试它)。

如果您使用过MVC,您可能有时会发现创建模型对象以反映视图的状态很有用,例如,显示和隐藏一些编辑对话框等。

MVVM模式只是将该实践推广到所有UI元素。

它不是Microsoft模式,附加的是WPF/Silverlight数据绑定特别适合使用这种模式。但没有什么能阻止您将其与Java服务器面孔一起使用。

我认为主要区别之一是在MVC中,您的V直接读取您的M,并通过C来操作数据,而在MVVM中,您的VM充当M代理,并为您提供可用的功能V。

如果我没有充满垃圾,我很惊讶没有人创建混合,您的VM只是一个M代理,C提供所有功能。

据我所知,MVVM映射到MVC的MV——这意味着在传统的MVC模式中,V不直接与M通信。在MVC的第二个版本中,M和V之间有直接链接。MVVM似乎承担了所有与M和V通信相关的任务,并将其耦合以将其与C解耦。实际上,仍然有更大范围的应用程序工作流程(或使用场景的实现)在MVVM中没有完全考虑。这是控制器的作用。通过从控制器中删除这些较低级别的方面,它们更干净,更容易修改应用程序的使用场景和业务逻辑,也使控制器更具可重用性。

MVC/MVVM不是要么/要么选择。

这两种模式在ASP. Net和Silverlight/WPF开发中以不同的方式出现。

对于ASP. Net,MVVM用于视图中的双向绑定数据。这通常是客户端实现(例如使用Knockout.js)。另一方面,MVC是分离关注点在服务端的一种方式。

对于Silverlight和WPF,MVVM模式更具包容性,并且可以出现作为MVC(或其他将软件组织成单独职责的模式)的替代品。这种模式经常出现的一个假设是ViewModel只是替换了MVC中的控制器(就好像你可以用VM替换首字母缩略词中的C,一切都会被原谅)…

ViewModel没有必须取代对单独控制器的需求。

问题是:为了独立可测试*,特别是在需要时可重用,视图模型不知道什么视图显示它,但更重要的是不知道它的数据从何而来

*注意:在实践中,控制器从ViewModel中删除了需要单元测试的大部分逻辑。然后VM变成了一个不需要测试(如果有的话)的哑容器。这是一件好事,因为VM只是设计者和编码器之间的桥梁,所以应该保持简单。

即使在MVVM中,控制器通常也会包含所有处理逻辑,并决定使用哪些视图模型在哪些视图中显示哪些数据。

到目前为止,我们已经看到ViewModel模式的主要好处是从XAML代码后面删除代码使XAML编辑成为更独立的任务。我们仍然在需要时创建控制器来控制(没有双关语)我们应用程序的整体逻辑。

我们遵循的基本MVCVM指南是:

  • 视图显示某种形状的数据。他们不知道数据来自哪里。
  • ViewModels保存一定形状的数据和命令,它们不知道数据或代码来自哪里,也不知道它是如何显示的。
  • 模型持有实际数据(各种上下文、存储或其他方法)
  • 控制器侦听和发布事件。控制器提供控制查看哪些数据和在哪里查看数据的逻辑。控制器向ViewModel提供命令代码,以便ViewModel实际上是可重用的。

我们还注意到雕塑代码根框架实现了MVVM和类似于Prism AND的模式,它还广泛使用控制器来分离所有用例逻辑。

不要假设控制器被视图模型淘汰了。

我已经开始了一个关于这个主题的博客,我将在可以的时候添加到博客中(仅在托管丢失时存档)。将MVCVM与常见导航系统相结合存在问题,因为大多数导航系统只使用视图和虚拟机,但我将在后面的文章中详细介绍。

使用MVCVM模型的另一个好处是在应用程序的生命周期中,只有控制器对象需要存在于内存中和控制器主要包含代码和很少的状态数据(即很小的内存开销)。这使得应用程序的内存密集型比必须保留视图模型的解决方案要少得多,并且它非常适合某些类型的移动开发(例如使用Silverlight/Prism/MEF的Windows Mobile)。这当然取决于应用程序的类型,因为您可能仍然需要保留偶尔缓存的虚拟机以获得响应能力。

注意:这篇文章已经被编辑了很多次,并没有专门针对所问的狭隘问题,所以我更新了第一部分,现在也涵盖了这一点。在下面的评论中,大部分讨论仅与ASP. Net有关,而不是更广泛的情况。这篇文章旨在涵盖MVVM在Silverlight、WPF和ASP. Net中的更广泛使用,并试图阻止人们用ViewModels替换控制器。

MVVMC,或者MVC+,似乎是企业和快速应用程序开发的可行方法。虽然将UI与业务和交互逻辑分开是很好的,但“纯”MVVM模式和大多数可用示例在单一视图上最有效。

不确定你的设计,但是我的大多数应用程序,然而,包含页面和几个(可重用)视图,因此ViewModel确实需要在某种程度上交互。使用页面作为控制器将完全违背MVVM的目的,因此不使用“VM-C”方法来进行底层逻辑可能会在应用程序成熟时导致具有挑战性的结构。即使在VB-6中,我们中的大多数人可能也停止了将业务逻辑编码到Button事件中,并开始将命令“中继”到控制器,对吧?我最近看了许多关于这个主题的新兴框架;我最喜欢的显然是Magellan(在co的ex)方法。编码快乐!

我认为理解这些首字母缩略词的最简单方法是暂时忘记它们。相反,想想它们起源的软件,它们中的每一个。这实际上归结为早期网络和桌面之间的区别。

随着2000年代中期的复杂性增长,MVC软件设计模式——首次描述于20世纪70年代——开始应用于网络应用程序。想想数据库、超文本标记语言页面,以及介于两者之间的代码。让我们稍微改进一下,以到达MVC:

让我们保留这个网页的一个特性,不是今天的样子,而是十年前的样子,当时JavaScript是一个卑鄙的,卑鄙的烦恼,真正的程序员很好地避开了:超文本标记语言页面本质上是愚蠢和被动的。浏览器是一个瘦客户端,或者如果你愿意,一个糟糕的客户端。浏览器中没有智能。整页重新加载规则。“视图”每次都重新生成。

让我们记住,这种网络方式,尽管风靡一时,但与桌面相比却非常落后。桌面应用程序是胖客户端,或者丰富的客户端,如果你愿意的话。(甚至像Microsoft Word这样的程序也可以被认为是某种客户端,文档客户端。)它们是充满智慧的客户端,对数据充满了解。它们是有状态的。它们将正在处理的数据缓存在内存中。没有重新加载整页这样的废话。

这种丰富的桌面方式可能是第二个首字母缩略词MVVM的起源。不要被字母所愚弄,因为C的遗漏。控制器仍然存在。它们需要。没有任何东西被删除。我们只是添加了一件事:状态,缓存在客户端上的数据(以及处理这些数据的智能)。这些数据,本质上是客户端上的缓存,现在被称为“ViewModel”。这就是允许丰富交互的原因。就是这样。

  • MVC=模型、控制器、视图=本质上是单向通信=交互性差
  • MVVM=模型、控制器、缓存、视图=双向通信=丰富的交互性

我们可以看到,通过Flash、Silverlight以及最重要的JavaScript,网络已经接受了MVVM。浏览器不能再被合理地称为瘦客户端。看看它们的可编程性。看看它们的内存消耗。看看现代网页上所有的Javascript交互性。

就我个人而言,我发现这个理论和首字母缩略词业务更容易理解,看看它在具体现实中指的是什么。抽象概念是有用的,尤其是在具体问题上得到证明时,所以理解可能会完整。

微软提供了在这里解释Windows环境中的MVVM模式

这里有一个关键部分:

在Model-ViewModel设计模式中,应用程序由以下部分组成在此处输入图像描述

  • 模型:这表示您的应用程序使用的数据模型。例如,在图片共享应用程序中,此层可能代表设备上可用的一组图片以及用于读取和使用的API写入图片库。

  • 查看:应用程序通常由多个UI页面组成。向用户显示的每个页面都是MVVM术语中的一个视图。视图是用于定义用户看到的内容和样式的XAML代码。数据从模型中显示给用户,这是ViewModel根据当前状态向UI提供此数据应用程序。例如,在图片共享应用程序中,视图将是UI向用户显示设备上的相册列表,其中的图片一个专辑,也许还有另一个向用户展示特定内容的专辑图片。

  • 视图模型:ViewModel将数据模型或简单的模型绑定到应用程序的UI或视图。它包含与用于管理模型中的数据并将数据作为集合公开XAML UI或视图可以绑定到的属性。例如,在图片共享应用程序中,ViewModel会公开相册列表,并为每个相册公开一个图片列表。UI是不可知论的这些照片是从哪里来的以及它们是如何被检索到的知道一组由ViewModel暴露的图片并显示它们给用户。

嗯,通常MVC用于Web开发,MVVM在WPF/Silverlight开发中最流行。但是,有时Web架构可能混合了MVC和MVVM。

例如:您可以使用knockout.js,在这种情况下,您将在客户端拥有MVVM。你的MVC的服务器端也可以改变。在复杂的应用程序中,没有人使用纯模型。使用ViewModel作为MVC的“模型”可能是有意义的,你的真实模型基本上将是这个VM的一部分。这给你一个额外的抽象层。

让我惊讶的是,这是一个高度投票的答案,没有提到MVVM的起源MVVM是微软社区中使用的流行术语,它是Martin Fowler的演示模型中的起源。因此,要了解该模式的动机以及与其他模式的差异,首先要阅读关于该模式的原始文章。

使用MVC将强类型的ViewModel注入View

  1. 控制器负责更新ViewModel并将其注入View。(对于get请求)
  2. ViewModel是DataContext和视图状态的容器,例如最后选定的项目等。
  3. 模型包含数据库实体,非常接近数据库模式,它执行查询和过滤。(我喜欢EF和LINQ)
  4. 模型还应该考虑存储库和/或将结果投影到强类型中(EF有一个很棒的方法…… EF。
  5. ViewModel获取数据并执行业务规则和验证
  6. 发回上的控制器将调用ViewModel Post方法并等待结果。
  7. 控制器将把新更新的视图模型注入到视图中。视图使用唯一强类型绑定
  8. 视图只是呈现数据,并将事件发布回控制器。
  9. MVC拦截入站请求并将其路由到正确的控制器强数据类型

在这个模型中,与请求或响应对象有不再有HTTP级别联系,因为MSFT的MVC机器对我们隐藏了它。

为澄清上文第6项(经请求)…

假设一个ViewModel像这样:

public class myViewModel{public string SelectedValue {get;set;}public void Post(){//due to MVC model binding the SelectedValue string above will be set by MVC model binding on post back.//this allows you to do something with it.DoSomeThingWith(SelectedValue);SelectedValue = "Thanks for update!";}}

帖子的控制器方法将是这样的(见下文),注意mvm的实例是由MVC绑定机制自动实例化的。结果你永远不必下拉到查询字符串层!这是MVC根据查询字符串为你实例化ViewModel!

[HTTPPOST]public ActionResult MyPostBackMethod (myViewModel mvm){if (ModelState.IsValid){// Immediately call the only method needed in VM...mvm.Post()}return View(mvm);}

请注意,为了让上面的这个操作方法按照你的意图工作,你必须定义一个空CTOR来初始化帖子中没有返回的东西。回发还必须回发那些改变的东西的名称/值对。如果有缺失的名称/值对,MVC绑定引擎会做正确的事情,这简直是什么都没有!如果发生这种情况,你可能会发现自己在说“我在回发上丢失了数据”…

这种模式的优点是ViewModel完成了与Model/Buisness逻辑接口的所有“杂乱”工作,控制器只是某种路由器。它是SOC。

从实用的角度来看,MVC(模型-视图-控制器)是一种模式。然而,当MVC用作ASP.netMVC时,当与实体框架(EF)和“强大工具”结合使用时,是一种非常强大的、部分自动化的方法,可以将数据库、表和列引入网页,仅用于完整的CRUD操作或R(检索或读取)操作。至少在我使用MVVM时,视图模型与依赖于业务对象的模型交互,这些模型又是“手工制作的”,经过大量努力,很幸运能得到与EF提供的“开箱即用”模型一样好的模型。从实际编程的角度来看,MVC似乎是一个不错的选择,因为它提供了很多开箱即用的实用程序,但仍有可能添加花里胡哨的功能。

作为对给出的许多回答的补充,我想从现代客户端Web或富Web应用程序的角度添加一些额外的视角。

实际上,如今简单的网站和更大的Web应用程序通常使用许多流行的库构建,例如Bootstrap。由Steve Sanderson构建的Knockout为MVVM模式提供了支持,该模式模仿了模式中最重要的行为之一:通过视图模型进行数据绑定。只需一点JavaScript,就可以实现数据和逻辑,然后可以将其添加到具有简单data-bind超文本标记语言属性的页面元素中,类似于使用Bootstrap的许多功能。仅这两个库一起提供交互式内容;当与路由相结合时,这种方法可以产生构建单页应用程序的简单而强大的方法。

类似地,像Angular这样的现代客户端框架按照惯例遵循MVC模式,但也添加了一个服务。有趣的是,它被吹捧为模型-视图-任何(MVW)。(见关于Stack Overflow的文章。)

此外,随着Angular 2等进步 Web框架的兴起,我们看到了术语的变化,也许是一种新的架构模式,其中组件由视图或模板组成,并与服务交互——所有这些都可以包含在模块中;一系列模块组成了应用程序。

简单的区别:(灵感来自Yaakov的Coursera AngularJS课程)

在此处输入图片描述

MVC(模型视图控制器)

  1. 车型:模型包含数据信息。不调用或使用控制器和视图。包含表示数据的业务逻辑和方法。其中一些数据可能以某种形式显示在视图中。它还可以包含从某些来源检索数据的逻辑。
  2. 控制器:充当视图和模型之间的连接。视图调用Controller,Controller调用模型。它基本上通知模型和/或视图根据需要进行更改。
  3. 查看:处理UI部分。与用户交互。

MVVM(模型视图视图模型)

视图模型

  1. 它是视图状态的表示。
  2. 它保存视图中显示的数据。
  3. 响应视图事件,也称为表示逻辑。
  4. 调用业务逻辑处理的其他功能。
  5. 永远不要直接要求视图显示任何内容。

MVC是受控环境,MVVM是反应式环境。

在受控环境中,你应该有更少的代码和一个共同的逻辑源;它们应该始终存在于控制器中。然而;在网络世界中,MVC很容易被分为视图创建逻辑和视图动态逻辑。创建存在于服务器上,动态存在于客户端上。你可以在ASP.NETMVC与AngularJS结合的情况下看到很多这种情况,而服务器将创建一个视图并传递一个模型并将其发送给客户端。然后客户端将与视图交互,在这种情况下,AngularJS作为本地控制器介入。一旦提交,模型或新模型将传递回服务器控制器并处理。(因此循环继续,当使用套接字或AJAX等时,这种处理有很多其他翻译,但总的来说,架构是相同的。)

MVVM是一个反应式环境,这意味着你通常编写将基于某些事件激活的代码(例如触发器)。在MVVM蓬勃发展的XAML中,这一切都可以通过内置的数据绑定框架轻松完成,但如前所述,这将适用于使用任何编程语言的任何View中的任何系统。它不特定于MS。ViewModel触发(通常是属性更改事件),View基于你创建的任何触发器对其做出反应。这可能是技术性的,但底线是View是无状态的,没有逻辑。它只是根据值更改状态。此外,ViewModel是无状态的,几乎没有逻辑,Model是逻辑本质上为零的状态,因为它们应该只维护状态。我将其描述为应用程序状态(模型)、状态转换器(ViewModel),然后是视觉状态/交互(视图)。

在MVC桌面或客户端应用程序中,你应该有一个模型,模型应该由控制器使用。基于模型,控制器将修改视图。视图通常与具有接口的控制器绑定,以便控制器可以使用各种视图。ASP.NETMVC的逻辑在服务器上稍微向后,因为控制器管理模型并将模型传递给选定的视图。然后视图充满基于模型的数据,并有自己的逻辑(通常是另一个MVC集,例如使用AngularJS完成)。人们会争论并将此与应用程序MVC混淆,并试图两者兼而有之,在这一点上维护项目最终将成为一场灾难。使用MVC时始终将逻辑和控件放在一个位置。不要在View后面的代码中编写View逻辑(或通过JS在View中)来容纳Controller或Model数据。让Controller更改View。唯一应该存在于View中的逻辑是通过它使用的接口创建和运行所需的任何东西。这方面的一个例子是提交用户名和密码。无论是桌面还是网页(在客户端),每当View触发Submit操作时,Controller都应该处理提交过程。如果做得正确,您总是可以轻松地找到围绕MVC Web或本地应用程序的方式。

MVVM是我个人最喜欢的,因为它是完全反应式的。如果一个模型改变了状态,ViewModel会监听并翻译该状态,仅此而已!!!然后视图会监听ViewModel的状态改变,它也会根据ViewModel的翻译进行更新。有些人称之为纯MVVM,但实际上只有一个,我不在乎你如何争论,它总是纯MVVM,视图绝对不包含逻辑。

这里有一个小例子:假设你想在按下按钮时滑入菜单。在MVC中,你的界面中将有一个MenuP的操作。控制器将知道当你单击菜单按钮时,然后告诉视图基于另一个界面方法(如SlideMenuIn)在菜单中滑动。往返的原因是什么?如果控制器决定你不能或想要做其他事情,这就是原因。控制器应该负责视图,除非控制器这样说,否则视图什么也不做。然而;在MVVM中,动画中的幻灯片菜单应该是内置的和通用的,而不是被告知将基于某些值将其滑入。因此,它会侦听ViewModel,当ViewModel说,IsMenuActive=true(或然而)时,就会发生动画。现在,有了这一点,我想再明确一点,请注意。IsMenuActive可能是糟糕的MVVM或ViewModel设计。在设计ViewModel时,你永远不应该假设View根本没有任何功能,而只是传递翻译的模型状态。这样,如果你决定更改你的视图以删除菜单并以另一种方式显示数据/选项,ViewModel并不关心。那么你将如何管理菜单?当数据有意义时,就是这样。因此,一种方法是给菜单一个选项列表(可能是一个内部ViewModel的数组)。如果该列表有数据,菜单就知道通过触发器打开,如果没有,它就知道通过触发器隐藏。你只是在ViewModel中有菜单的数据。不要决定在ViewModel中显示/隐藏该数据…通过这种方式,View是完全响应式和通用的,可以在许多不同的情况下使用。

所有这一切可能是绝对没有意义的,如果你还没有至少稍微熟悉每个架构和学习它可以是非常混乱的,因为你会发现很多坏的信息在网上。

所以……要记住这一点。预先决定如何设计您的应用程序并坚持下去。

如果你做MVC,这很棒,然后确保你的控制器是可管理的,并完全控制你的视图。如果你有一个大的视图,考虑向具有不同控制器的视图添加控件。只是不要将这些控制器级联到不同的控制器上。维护非常令人沮丧。花点时间,以一种将作为单独组件工作的方式单独设计事物……并且总是让控制器告诉模型提交或持久存储。MVC的理想依赖设置是查看←控制器→模型或ASP.NET(不要让我开始)模型←视图↔控制器→模型(其中模型可以是控制器到视图的相同或完全不同的模型)……当然,此时唯一需要知道Controller in View的主要是端点参考,以知道返回哪里传递模型。

如果你做MVVM,我祝福你善良的灵魂,但花时间做对!不要使用接口。让你的视图根据值决定它的外观。使用模拟数据玩视图。如果你最终有一个视图向你显示一个菜单(如示例所示),即使你当时不想要它,那么很好。你的视图正在按照它应该的方式工作,并根据它应该的值做出反应。只需向你的触发器添加更多要求,以确保当ViewModel处于特定翻译状态时不会发生这种情况,或命令ViewModel清空此状态。不要使用内部逻辑将其删除,就好像你正在从那里决定View是否应该看到它一样。记住,你不能假设ViewModel中是否有菜单。最后,模型应该只允许你更改并很可能存储状态。这是验证和所有将发生的地方;例如,如果模型无法修改状态,那么它将简单地将自己标记为脏或其他什么。当ViewModel意识到这一点时,它将翻译什么是脏的,然后View将意识到这一点并通过另一个触发器显示一些信息。视图中的所有数据都可以绑定到ViewModel,所以一切都可以是动态的,只有模型和ViewModel完全不知道视图将如何对绑定做出反应。事实上,模型也不知道ViewModel。在设置依赖项时,它们应该像这样指向,只像这样指向视图→视图模型→模型(这里还有一个侧面说明……这可能也会引起争论,但我不在乎……不要将模型传递给视图,除非该模型是不可变的;否则用适当的ViewModel包装它。视图不应该看到模型周期。我给老鼠破解你看过什么演示或你是如何做到的,那是错误的。

这是我最后的建议……看看一个设计良好但非常简单的MVC应用程序,并对MVVM应用程序做同样的事情。一个将拥有更多的控制权,灵活性有限到零,而另一个将没有控制权,灵活性无限。

受控环境有利于从一组控制器或(单个源)管理整个应用程序,而反应式环境可以分解为单独的存储库,完全不知道应用程序的其余部分在做什么。微观管理与自由管理。

如果我还没有让你足够困惑,试着联系我……我不介意用插图和例子来详细说明这一点。

归根结底,我们都是程序员,在编码时,这种无政府状态生活在我们心中……所以规则会被打破,理论会改变,所有这些都将最终成为猪洗……但是当在大型项目和大型团队中工作时,就设计模式达成一致并执行它真的很有帮助。总有一天,它会让一开始采取的小步骤成为以后节省的飞跃。

我曾经认为MVC和MVVM是一样的。现在因为Flux的存在,我可以分辨出区别:

在MVC中,对于应用程序中的每个视图,你都有一个模型和一个控制器,所以我称之为视图,视图模型,视图控制器。模式并没有告诉你一个视图如何与另一个视图通信。因此,在不同的框架中,有不同的实现。例如,在有些实现中,控制器相互通信,而在其他实现中,有另一个组件在它们之间进行调解。甚至有一些实现中,视图模型相互通信,这是对MVC模式的打破,因为视图模型应该只被视图控制器访问。

在MVVM中,你也有每个组件的视图模型。该模式没有指定视图应该如何影响视图模型,所以通常大多数框架只在视图模型中包含控制器的功能。然而,MVVM确实告诉你,你的视图模型的数据应该来自模型,即不知道或自定义特定视图的整个模型。

为了演示区别,让我们以Flux模式为例。Flux模式告诉应用程序中不同的视图应该如何通信。每个视图侦听一个存储并使用调度器触发操作。调度器反过来告诉所有存储刚刚做出的操作,存储自己更新。Flux中的存储对应于MVVM中的(通用)模型。它不是为任何特定视图定制的。所以通常当人们使用React和Flux时,每个React组件实际上都实现了MVVM模式。当一个操作发生时,视图模型调用调度器,最后它根据存储中的变化得到更新,这就是模型。不能说每个组件都实现了MVC,因为在MVC中只有控制器才能更新视图模型。所以MVVM可以和Flux一起工作(MVVM处理视图和视图模型之间的通信,Flux处理不同视图之间的通信),而MVC不能和Flux一起工作,否则会破坏一个关键原则。

mvc是服务器端,mvvm是Web开发中的客户端(浏览器)。

大多数时候javascript用于浏览器中的mvvm。有许多用于mvc的服务器端技术。

对于一个不太熟悉架构模式主题的人来说,其他答案可能不容易理解。不熟悉应用程序架构的人可能想知道它的选择如何在实践中影响她的应用程序,以及社区中的所有大惊小怪。

为了阐明上述内容,我制作了这个涉及MVVM、MVP和MVC的剧本。故事从用户点击电影搜索应用程序中的“FIND”按钮开始……:

用户:点击…

查看:那是谁?[MVVM|MVP|MVC]

用户:我刚刚点击了搜索按钮…

查看:好的,等一下 … . [MVVM|MVP|MVC]

查看呼叫视图模型|演示者|控制器 … ) [MVVM|MVP|MVC]

查看:嘿视图模型|演示者|控制器,用户刚刚点击了搜索按钮,我该怎么办?[MVVM|MVP|MVC]

视图模型|演示者|控制器:嘿查看,那个页面上有搜索词吗?[MVVM|MVP|MVC]

查看:是的,……在这里……“钢琴”[MVVM|MVP|MVC]

--这是MVVMMVP|MVC之间最重要的区别---

演示者|控制器:谢谢查看,…同时我正在查找模型上的搜索词,请给他/她一个进度条[MVP|MVC]

演示者|控制器正在呼叫模型 … ) [MVP|MVC]

视图模型:谢谢,我会在模型上面查找搜索词的,但是不会直接跟你更新。我会触发事件来搜索ResultsListObservable,如果有结果的话。所以你最好观察一下。[MVVM]

(在观察search chResultsListObservable中的任何触发器时,查看认为它应该向用户显示一些进度条,因为视图模型不会就此与它交谈)

——————————————————————————————

视图模型|演示者|控制器:嘿模型,你有这个搜索词的匹配吗?:"钢琴"[MVVM|MVP|MVC]

模型:嘿视图模型|演示者|控制器,让我看看…[MVVM|MVP|MVC]

模型正在查询电影数据库 … ) [MVVM|MVP|MVC]

(过了一会儿……)

----这是MVVMMVPMVC之间的发散点-----

模型:我为你找到了一个列表,视图模型|演示者,这里是JSON"[{"name":"Piano老师","年份": 2001},{"name":"Piano","年份": 1993}]"[MVVM|MVP]

模型:有一些可用的结果,Controller。我在我的实例中创建了一个字段变量并用结果填充它。它的名字是“搜索结果列表”[MVC]

演示者|控制器谢谢模型,回到查看)[MVP|MVC]

演示者:感谢您的等待查看,我为您找到了一个匹配结果列表,并将它们排列成一个可展示的格式:[“钢琴老师2001”,“钢琴1993”]。也请现在隐藏进度条[MVP]

控制器:感谢您的等待查看,我已经向Model询问了您的搜索查询。它说它找到了一个匹配结果的列表,并将它们存储在其实例内名为“search chResultsList”的变量中。您可以从那里获取它。还请现在隐藏进度条[MVC]

视图模型:任何在搜索ResultsListObservable上的观察者都会收到通知,有这个新的列表以可呈现的格式出现:["Piano教师2001","Piano 1993"]。

查看:非常感谢Presenter[MVP]

查看:谢谢“控制器”[MVC](现在查看正在质疑自己:我应该如何向用户展示我从模型中得到的结果?电影的制作年份应该排在第一还是最后…?)

查看:哦,有一个新的触发器在search chResultsListObservable…,很好,有一个可呈现的列表,现在我只需要在列表中显示它。我也应该隐藏进度条,现在我有结果了。[MVVM]

如果您有兴趣,我写了一系列文章这里,通过实现电影搜索Android应用程序来比较MVVM、MVP和MVC。

控制器不会被MVVM中的ViewModel替换,因为ViewModel具有与控制器完全不同的功能。你仍然需要一个控制器,因为没有控制器,你的模型、ViewModel和View将没有多大作用…在MVVM中,你也有一个控制器,MVVM这个名字只是误导。

MVVMC是正确的名字在我的愚见。

如您所见,ViewModel只是MVC模式的补充。它将转换逻辑(例如将对象转换为字符串)从Controller移动到ViewModel。

简而言之-在MVC控制器中知道(控件)视图,而在MVVM中,ViewModel不知道谁使用它。ViewModel向任何可能有兴趣使用它的人公开其可观察的属性和操作。这一事实使得测试更容易,因为ViewModel中没有对UI的引用。

MVVM

  1. 查看➡ViewModel➡Model
  • 视图具有对ViewModel的引用,但反之亦然。
  • ViewModel具有对Model的引用,但反之亦然。
  • 视图没有引用模型,反之亦然。
  1. 如果您使用的是控制器,它可以引用意见运营模式,尽管控制器并不总是必要的,如SwiftUI所示。
  2. 数据绑定:我们为ViewModel Properties创建侦听器,以便数据可以通过视图模型从视图流向模型。虽然引用是单向的:View➡ViewModel➡Model,但数据需要流动:View↔ViewModel↔Model。很明显,视图如何通过读取自己的属性从模型获取数据。数据绑定是如何检测视图中的事件并将它们反馈给模型。
class CustomView: UIView {var viewModel = MyViewModel {didSet {self.color = viewModel.viewColor}}
convenience init(viewModel: MyViewModel) {self.viewModel = viewModel}}

struct MyViewModel {var viewColor: UIColor {didSet {colorChanged?() // This is where the binding magic happens.}}   
var colorChanged: ((UIColor) -> Void)?}

class MyViewController: UIViewController {
let myViewModel = MyViewModel(viewColor: .green)let customView: CustomView!
override func viewDidLoad() {super.viewDidLoad()
// This is where the binder is assigned.myViewModel.colorChanged = { [weak self] color inprint("wow the color changed")}customView = CustomView(viewModel: myViewModel)self.view = customView}}

设置差异

  1. 业务逻辑保存在MVC的控制器和MVVM的ViewModels中。
  2. 事件直接从View传递到MVC中的控制器,而事件从View传递到ViewModel传递到MVVM的Controller(如果有)。

共同特点

  1. MVVM和MVC都不允许View直接向Model/s发送消息。
  2. 两者都有模型。
  3. 两人都有观点。

MVVM的优势

  1. 因为ViewModel包含业务逻辑,所以它们是更小的具体对象,这使得它们易于单元测试。另一方面,在MVC中,业务逻辑在ViewController中。如果不同时测试所有方法和侦听器,你怎么能相信视图控制器的单元测试是全面安全的?你不能完全信任单元测试结果。
  2. 在MVVM中,由于业务逻辑从Controller中抽取到原子ViewModel单元中,因此ViewController的大小会缩小,这使得ViewController代码更加清晰。

MVC的优势

  1. 在控制器中提供业务逻辑减少了对分支的需求,因此语句更有可能在缓存上运行,这比将业务逻辑封装到ViewModel中更具性能。
  2. 在一个地方提供业务逻辑可以加速不需要测试的简单应用程序的开发过程。我不知道什么时候不需要测试。
  3. 在ViewController中提供业务逻辑对于新开发人员来说更容易考虑。