标准的“模型视图控制器”(MVC)模式和微软的Model/View/ViewModel(MVVM)模式有区别吗?
标准的“模型视图控制器”(MVC)模式和微软的Model/View/ViewModel(MVVM)模式有区别吗?
这两种模式在ASP.Net和Silverlight/WPF开发中以不同的方式出现。
对于ASP.Net,MVVM用于视图内部的双向数据绑定。通常这是一个客户端实现(例如使用Knockout.js)。而MVC则是一种在服务器端分离关注点的方式。
对于Silverlight和WPF,MVVM模式更为全面,可以看作是替代MVC(或者其他将软件分成独立职责的模式)的方式。一个常见的误解是,ViewModel仅仅替换了MVC中的控制器(好像你只需要在首字母缩写中用VM替换C就行了)...
问题在于:为独立测试*和特别是需要重用时,视图模型不知道显示它的视图是什么,更重要的是,也不知道它的数据来自哪里。
*注意:实际上,控制器从ViewModel中移除了大部分需要进行单元测试的逻辑。因此,VM变成了一个简单的容器,几乎不需要进行任何测试。这是一件好事,因为VM只是设计师和编码人员之间的桥梁,所以应该保持简单。
即使在MVVM中,控制器通常也包含所有处理逻辑,并决定在哪些视图中使用哪些视图模型显示哪些数据。
从我们目前看到的情况来看,ViewModel模式的主要好处是将代码从XAML代码后台中移除,从而使XAML编辑成为一个更独立的任务。我们仍然会根据需要创建控制器来控制应用程序的总体逻辑。
我们还注意到,Sculpture 代码生成框架 实现了 MVVM 模式和类似于 Prism 的模式,并且它还广泛使用控制器来分离所有用例逻辑。
我已经开始写关于这个主题的博客,在以后我会继续添加文章(仅存档因为托管丢失)。将 MVCVM 与常见导航系统结合存在问题,因为大多数导航系统只使用 Views 和 VMs,但我将在以后的文章中详细探讨这个问题。
使用 MVCVM 模式的另一个好处是,只有控制器对象需要在应用程序的生命周期内存在,并且控制器主要包含代码和很少的状态数据(即很小的内存开销)。这使得应用程序比需要保留视图模型的解决方案具有更少的内存消耗,并且它非常适合某些类型的移动开发(例如使用 Silverlight/Prism/MEF 的 Windows Mobile)。当然,这取决于应用程序的类型,因为您可能仍然需要保留偶尔缓存的 VM 以提高响应速度。
注意:这篇文章被多次编辑过,并且没有针对狭窄的问题进行具体说明,因此我已经更新了第一部分来涵盖它。下面的评论中许多讨论只涉及ASP.Net而不是更广泛的情况。本文旨在探讨在Silverlight、WPF和ASP.Net中更广泛地使用MVVM,并试图劝阻人们用ViewModels替换控制器。
我认为理解这些缩写的最简单方法是先暂时抛开它们。相反,想一想它们所起源的软件,它们中的每一个。实际上,这只涉及早期Web和桌面之间的区别。
随着它们在2000年代中期变得越来越复杂,MVC软件设计模式 - 首次描述于1970年代 - 开始应用于Web应用程序。想想数据库、HTML页面和代码之间的关系。让我们稍微完善一下,以达到MVC:对于“数据库”,让我们假设数据库加上接口代码。对于“HTML页面”,让我们假设HTML模板加上模板处理代码。对于“代码之间”,让我们假设代码映射用户点击到行动,可能会影响数据库,肯定会导致显示另一个视图。至少在这个比较的目的上就是这样。
让我们保留这个Web方式的一个特征,不是像今天这样存在,而是10年前存在的样子,当时JavaScript是一种低劣、可鄙的烦恼,真正的程序员最好远离它:HTML页面基本上是愚蠢且被动的。浏览器是一个轻客户端,或者说,一个贫客户端。浏览器中没有智能。全页面刷新是主流。每次都要重新生成“视图”。
让我们记住,尽管这种Web方式很流行,但与桌面相比,它非常落后。桌面应用程序是肥客户端或富客户端。 (即使像Microsoft Word这样的程序也可以被认为是某种类型的客户端,一个文档客户端。)它们充满了智能,充满了关于数据的知识。它们是有状态的。它们将处理的数据缓存在内存中。完全没有全页面刷新这种鬼东西。
而这种富桌面方式可能就是第二个缩写MVVM的起源。不要被字母所迷惑,也不要因为省略了C而被愚弄。控制器仍然存在。他们必须存在。没有任何东西被删除。我们只是增加了一件事:状态,客户端上缓存的数据(以及处理该数据的智能)。那些数据,本质上是客户端上的高速缓存,现在被称为“ViewModel”。它是允许丰富互动性的东西。 就是这样。
我们可以看到,通过Flash、Silverlight和最重要的JavaScript,Web已经采用了MVVM。浏览器不再合理地被称为轻客户端。看看它们的可编程性,看看它们的内存消耗,看看现代网页上所有的Javascript互动性。
个人认为,通过观察具体事物来理解这些理论和缩写更容易理解。抽象的概念很有用,尤其是在具体事物上进行演示时,理解可能会变得更加全面。
MVVM 模型-视图-视图模型 与MVC(模型-视图-控制器)类似。
控制器 被 视图模型 替代。视图模型位于UI层下方。视图模型公开视图所需的数据和命令对象。您可以将其视为视图从中获取数据和操作的容器对象。视图模型从模型中提取其数据。
Russel East 写了一篇博客,详细讨论了MVVM与MVC不同之处。
首先,MVVM是MVC模式的一种进阶,它使用XAML来处理显示。 这篇文章概述了两种模式的某些方面。
Model/View/ViewModel架构的主要重点似乎在于,在数据(“Model”)之上,有另一层非可视组件(“ViewModel”),将数据的概念更紧密地映射到数据的视图的概念(“View”)。由View绑定到ViewModel,而不是直接绑定到Model。
Microsoft在此提供了Windows环境下MVVM设计模式的解释。
以下是其中关键部分:
在Model-View-ViewModel设计模式中,应用程序由三个通用组件组成。
Model:表示应用程序消耗的数据模型。例如,在图片共享应用中,此层可能代表设备上可用的图片集和用于读取和写入图片库的API。
View:应用程序通常由多个UI页面组成。每个向用户显示的页面都是MVVM术语中的一个视图。视图是用于定义和样式化用户所见内容的XAML代码。从模型中获取的数据显示给用户,而ViewModel的工作是根据应用程序的当前状态向UI提供此数据。例如,在图片共享应用中,视图将是向用户显示设备上相册列表、相册中的图片以及可能向用户显示特定图片的UI。
ViewModel:ViewModel将数据模型(或简单地说是模型)与应用程序的UI(或视图)绑定。它包含用于管理来自模型的数据并将数据作为一组属性暴露给XAML UI(或视图)的逻辑。例如,在图片共享应用中,ViewModel将公开相册列表,并且对于每个相册,公开图片列表。UI不知道图片来自何处以及如何检索它们。它只知道由ViewModel公开的一组图片,并向用户显示它们。
我认为其中一个主要区别在于,在MVC中,你的V直接读取你的M,并通过C操作数据,而在MVVM中,你的VM作为M代理,同时向你的V提供可用功能。
如果我没有胡说的话,我很惊讶没有人创建一个混合版本,其中你的VM仅是一个M代理,而C提供所有功能。
Presenter|Controller: 谢谢View,与此同时我正在Model上查找搜索词,请向用户展示进度条。[MVP|MVC]
(Presenter|Controller正在调用Model...) [MVP|MVC]
ViewModel: 谢谢,我将在Model上查找搜索词,但不会直接更新您。相反,如果有任何结果,我将触发searchResultsListObservable的事件。因此,最好在那里观察。[MVVM]
(当观察到searchResultsListObservable中的任何触发器时,View认为应向用户显示一些进度条,因为ViewModel不会直接通知它)
——————————————————————————————
ViewModel|Presenter|Controller: 嘿,Model,你对这个搜索词“piano”有任何匹配吗?[MVVM|MVP|MVC]
Model: 嘿,ViewModel|Presenter|Controller,让我查一下...[MVVM|MVP|MVC]
(Model正在向电影数据库查询...) [MVVM|MVP|MVC]
(过了一会儿...)
———— 这是MVVM、MVP和MVC的分歧点 ————–
模型:我为你找到了一个列表,视图模型|展示者,这里是它的JSON格式:“[{“name”:”钢琴老师”,”year”:2001},{“name”:”钢琴”,”year”:1993}]” [MVVM|MVP]
模型:有一些结果可以用,控制器。我已经在我的实例中创建了一个字段变量,并将其填充了该结果。它的名称是“searchResultsList” [MVC]
(展示者|控制器感谢模型并返回视图) [MVP|MVC]
展示者:谢谢你等待,视图,我为你找到了一系列匹配的结果,并以一个可呈现的格式排列好了它们:“钢琴老师 2001”、“钢琴 1993”。请现在隐藏进度条 [MVP]
控制器:谢谢你等待,视图,我已经向模型询问了你的搜索查询。它说找到了一系列匹配的结果,并将它们存储在其实例内部的一个名为“searchResultsList”的变量中。你可以从那里获取它。请现在隐藏进度条 [MVC]
视图模型:任何对searchResultsListObservable的观察者都会被通知,有这个新列表以可呈现的格式:“钢琴老师 2001”、“钢琴 1993”。[MVVM]
视图:非常感谢Presenter [MVP]
视图:感谢“控制器” [MVC](现在,视图正在自问:我应该如何向用户呈现从模型得到的结果?电影的生产年份应该排在最前面还是最后面...?)
视图:哦,搜索结果列表中有一个新的触发器......好了,有一个可呈现的列表,现在我只需要将其显示在列表中。既然我已经得到了结果,我也应该隐藏进度条。[MVVM]
如果您感兴趣,我写了一系列文章这里,通过实现一个电影搜索Android应用程序来比较MVVM、MVP和MVC。
MVC是受控环境,MVVM是反应性环境。
在受控环境中,代码量应该较少,逻辑的来源应该是通用的。这些都应该位于控制器中。然而,在Web世界中,MVC很容易分成视图创建逻辑和视图动态逻辑两部分。创建逻辑位于服务器上,动态逻辑位于客户端上。例如,在使用ASP.NET MVC和AngularJS时,服务器将创建一个视图并传递一个模型到客户端。客户端将与视图交互,此时AngularJS作为本地控制器介入。提交后,模型或新模型被传回服务器控制器并处理。(因此,当与套接字或AJAX等工作时有很多其他的处理方式,但总体架构相同)
MVVM是一种反应性环境,意味着通常会编写代码(如触发器),以响应某些事件。在XAML中,MVVM繁荣发展,这里可以轻松地使用内置的数据绑定框架完成所有操作,但正如前面提到的,这在任何系统中的任何视图中使用任何编程语言都可以实现。视图模型会触发(通常是属性更改事件),而视图会根据您创建的任何触发器对其进行反应。这可能会变得很技术性,但归根结底,视图是无状态而且没有逻辑的,它只是根据值改变状态。此外,视图模型是无状态的,几乎没有逻辑,而模型则是具有零逻辑的状态,因为它们只应维护状态。我将这描述为应用程序状态(模型),状态转换器(视图模型)以及视觉状态/交互(视图)。
在MVC桌面或客户端应用程序中,应该有一个模型,并且控制器应该使用该模型。基于模型,控制器将修改视图。视图通常使用接口与控制器相连,以便控制器可以与各种视图一起使用。在ASP.NET中,MVC的逻辑略微倒置,因为控制器管理模型并将模型传递给选定的视图。然后,视图根据模型填充数据并具有自己的逻辑(通常是另一个MVC集,例如AngularJS)。人们会争论并混淆这个问题,试图同时做两件事,此时维护项目最终会变成一场灾难。当使用MVC时,始终将逻辑和控制放在一个位置上。不要在视图的代码后台(或通过Web中的JS视图)中编写视图逻辑以适应控制器或模型数据。让控制器改变视图。唯一应该存在于视图中的逻辑是所需接口的创建和运行。例如,提交用户名和密码。无论是桌面还是Web页面(在客户端),控制器都应处理提交过程,每当视图触发提交操作时。如果做得正确,您始终可以轻松地找到MVC Web或本地应用程序的方法。
MVVM是我个人最喜欢的模式,因为它完全是响应式的。如果Model状态发生变化,ViewModel会监听并翻译该状态,然后View会根据来自ViewModel的翻译进行更新。有些人称之为纯MVVM,但实际上只有一种MVVM,我不在乎你怎么争辩,而且它始终是纯MVVM,其中View不包含任何逻辑。控制环境适合通过一组控制器或(单个来源)管理整个应用程序,而反应性环境可以分为不同的存储库,完全不知道应用程序的其余部分正在做什么。微观管理与自由管理。
如果我还没有让你感到困惑,请联系我...我不介意详细说明并提供插图和实例。
归根结底,我们都是程序员,在编码时会存在无政府主义...因此规则会被打破,理论会改变,所有这些最终都将变得毫无意义...但是在处理大型项目和大型团队时,确实有助于达成设计模式一致并强制执行。某一天,初期采取的小额外步骤将使节省成本跨越巨大的进展。
MVVM是Presentation Model模式的一种改进(有争议)。我说有争议,因为唯一的区别在于WPF提供了数据绑定和命令处理的能力。