标准的“模型视图控制器”模式和微软的模型/视图/视图模型模式之间有区别吗?


当前回答

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

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

其他回答

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

控制器被替换为ViewModel。ViewModel位于UI层下面。ViewModel公开视图所需的数据和命令对象。你可以把它想象成一个容器对象视图从它那里获取数据和动作。ViewModel从模型中提取数据。

Russel East写了一篇博客,详细讨论了为什么MVVM不同于MVC

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

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

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

在MVC桌面或客户端应用程序中,你应该有一个模型,该模型应该由控制器使用。基于模型,控制器将修改视图。视图通常与带有接口的控制器绑定,以便控制器可以使用各种视图。在ASP。NET中的MVC逻辑在服务器上略微向后,因为控制器管理模型并将模型传递给选定的视图。然后,视图会根据模型填充数据,并拥有自己的逻辑(通常是另一个MVC集,例如用AngularJS完成的)。人们会争论并将其与应用程序MVC混淆,并试图同时做这两件事,而维护项目最终将成为一场灾难。使用MVC时,总是把逻辑和控件放在一个位置。不要在视图后面的代码中(或通过web的JS在视图中)写视图逻辑来容纳控制器或模型数据。让控制器改变视图。视图中应该存在的唯一逻辑是通过它所使用的接口创建和运行所需的任何逻辑。提交用户名和密码就是一个例子。无论是桌面还是网页(在客户端),只要视图触发提交动作,控制器就应该处理提交过程。如果做得正确,你总是可以很容易地找到MVC web或本地应用程序。

MVVM是我个人最喜欢的,因为它完全是反应性的。如果模型改变了状态,ViewModel会监听并转换状态,就这样!!然后,视图监听ViewModel的状态变化,并根据来自ViewModel的转换进行更新。有些人称之为纯粹的MVVM,但实际上只有一个,我不在乎你怎么争论,它总是纯粹的MVVM,其中视图完全不包含逻辑。

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

如果你还不熟悉它们的架构,那么这一切可能完全没有意义,因为你会在网上发现很多不好的信息,学习起来可能会非常混乱。

所以…要做到这一点,需要记住以下几点。预先决定如何设计应用程序,并坚持下去。

如果你使用MVC,这很好,那么确保你的控制器是可管理的,并完全控制你的视图。如果你有一个大的视图,可以考虑在视图中添加具有不同控制器的控件。只是不要将这些控制器级联到不同的控制器上。维护起来非常令人沮丧。花点时间把东西分开设计,让它们作为独立的组件工作……并且总是让控制器告诉模型提交或持久化存储。MVC的理想依赖设置是视图←控制器→模型或ASP。模型←视图↔控制器→模型(从控制器到视图,模型可以相同,也可以完全不同)…当然,此时唯一需要知道的是视图中的控制器,主要是为了端点参考,以知道在哪里返回传递模型。

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

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

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

如果我还没有把你弄糊涂,试着联系我……我不介意用插图和例子详细讨论这个问题。

在一天结束的时候,我们都是程序员,当我们编码的时候,我们内心就会出现混乱……所以规则会被打破,理论会改变,所有这些最终都将成为垃圾……但在大型项目和大型团队中工作时,就设计模式达成一致并实施它确实很有帮助。总有一天,它会让你开始时多迈出的一小步,在以后变成一笔突飞猛进的积蓄。

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

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

为了更好地解释上述问题,我编写了这个涉及MVVM、MVP和MVC的剧本。故事开始于一个用户点击电影搜索应用程序中的“FIND”按钮…

用户:单击…

观点:那是谁?[MVVM|MVP|MVC]

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

视图:好的,稍等... .(MVVM MVP | | MVC)

(视图调用ViewModel|Presenter|Controller…)

视图:嘿,ViewModel|Presenter|Controller,一个用户刚刚点击了搜索按钮,我该怎么做?(MVVM MVP | | MVC)

ViewModel|Presenter|Controller:嘿View,这个页面上有搜索词吗?(MVVM MVP | | MVC)

查看:是的,…在这里…" piano " [MVVM|MVP|MVC]

这是MVVM和MVP|MVC之间最重要的区别

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

(演示者|控制器正在调用模型…)

ViewModel:谢谢,我会在模型上查找搜索词,但不会直接更新你。相反,如果有任何结果,我将触发事件来searchResultsListObservable。所以你最好仔细观察一下。(MVVM)

(当在searchResultsListObservable中观察任何触发器时,视图认为它应该向用户显示一些进度条,因为ViewModel不会在那上面与它对话)

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

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

Model: Hey ViewModel|Presenter|Controller, let me check…[MVVM|MVP|MVC]

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

(过了一会儿……)

————这是MVVM, MVP和MVC————-之间的分歧点

模型:我为你找到了一个列表,ViewModel|Presenter,这里是JSON“[{" name ": "钢琴老师","年份":2001},{" name ": "钢琴","年份":1993}]" [MVVM|MVP]

模型:有一些结果可用,控制器。我已经在我的实例中创建了一个字段变量,并将其填充为结果。它的名字是searchResultsList [MVC]

(演示者|控制器感谢模型并返回视图)[MVP|MVC]

主持人:感谢您的等待,View,我为您找到了匹配结果的列表,并将它们以像样的格式排列:["钢琴老师2001″,"钢琴1993 "]。同时,现在请隐藏进度条[MVP]

控制器:感谢等待视图,我已经询问了模型关于你的搜索查询。它说它已经找到了一个匹配结果列表,并将它们存储在其实例内名为“searchResultsList”的变量中。你可以从那里得到它。同时,现在请隐藏进度条[MVC]

ViewModel: searchResultsListObservable上的任何观察者都将被通知有这样一个像样格式的新列表:[" Piano Teacher 2001″," Piano 1993 "]。

视图:非常感谢主讲人[MVP]

视图:谢谢你“控制器”[MVC](现在视图在问自己:我应该如何将我从模型得到的结果呈现给用户?电影的制作年份是先写还是后写?)

视图:哦,在searchResultsListObservable中有一个新的触发器…,很好,有一个像样的列表,现在我只需要在列表中显示它。有了结果之后,我还应该隐藏进度条。(MVVM)

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

在web开发中,MVC是服务器端,MVVM是客户端(浏览器)。

大多数情况下,浏览器中的MVVM使用javascript。mvc有很多服务器端技术。