为什么要使用MVVM?


77

好的,我一直在研究MVVM模式,每次以前尝试进行研究时,出于以下原因我都放弃了:

  1. 不必要的超长编码
  2. 对于编码人员没有明显的优势(我的办公室没有设计师。目前只有我自己很快将成为另一个编码人员)
  3. 良好实践的资源/文档不多!(或者至少很难找到)
  4. 无法想到一个有利的单一方案。

我将再次放弃它,以为我要问是否有人回答了上述原因。

老实说,我看不出将其用于单个/合作伙伴编码的优势。即使在具有十个窗口的复杂项目中。对我来说,DataSet是一个足够好的视图和绑定,就像布伦特在以下问题中的回答一样

有人可以显示一个示例,与XAML DataBinding相比,使用MVVM模式可以节省时间。

目前,我100%的绑定是在XAML中完成的。因此,我不认为VM的意义在于它只是我需要编写和依赖的额外代码。

编辑:
在花了一个下午的时间研究MVVM之后,我终于找到了使我意识到此答案的真正好处的东西。


16
如果您已经对其进行了多次评估,并且没有看到使用它的优势,请不要使用它。
Daniel Daranas 2010年

3
为什么要使用多个问号?这似乎是相当一些问题,显示为相关的重复:stackoverflow.com/questions/1770857/...stackoverflow.com/questions/1644453/...例如
本杰明Podszun

1
@Daniel我知道,但我希望有一些示例场景希望改变主意并真正实现它!
Michal Ciechan 2010年

3
WPF在大多数情况下均采用MVVM。例如,使用不带MVVM的wpf树控件,您将在一天内将头发撕裂。MVVM可使事情变得更简单且可测试。
Gishu

2
这可能会回答一些问题:wintellect.com/CS/blogs/jlikness/archive/2010/04/14/…。我将其发布为评论,因为它只是一个链接,而不是真实的答案。
彼得

Answers:


105

概要

  • 所有模式的使用都是视情况而定的,好处(如果有)始终在于降低复杂性。
  • MVVM指导我们如何在GUI应用程序的类之间分配职责。
  • ViewModel将模型中的数据投影为适合View的格式。
  • 对于琐碎的项目,不需要MVVM。仅使用视图就足够了。
  • 对于简单项目,可能不需要拆分ViewModel / Model,仅使用Model和View就足够了。
  • Model和ViewModel不需要从一开始就存在,可以在需要时引入。

何时使用模式以及何时避免使用模式

对于足够简单的应用程序,每个设计模式都是多余的。假设您编写了一个GUI应用程序,该应用程序显示一个按钮,当按下该按钮时将显示“ Hello world”。在这种情况下,诸如MVC,MVP,MVVM之类的设计模式都会增加很多复杂性,而不会增加任何价值。

通常,仅仅因为某种程度上合适就引入设计模式总是一个错误的决定。应使用设计模式来降低复杂性,方法是直接降低整体复杂性,或者用熟悉的复杂性代替不熟悉的复杂性。如果设计模式无法通过这两种方式中的任何一种降低复杂性,请不要使用它。

为了解释熟悉和不熟悉的复杂性,请采用以下两个字符序列:

  • “ D.€|Ré%dfà?c”
  • “ CorrectHorseBatteryStaple”

虽然第二个字符序列的长度是第一个字符序列的两倍,但它比第一个字符序列更易于阅读,编写和记忆,所有这些都是因为它比较熟悉。对于代码中熟悉的模式也是如此。

当您认为熟悉程度取决于读者时,此问题会更上一层楼。一些读者会发现“ 3.14159265358979323846264338327950”比上述任何一个密码都更容易记住。有些不会。因此,如果您想使用一种MVVM,请尝试使用一种以您正在使用的特定语言和框架来镜像其最常见形式的镜像。

MVVM

就是说,让我们通过示例深入探讨MVVM的主题。MVVM指导我们如何在GUI应用程序中的类之间(或在各层之间-稍后再详细介绍)分配职责,目标是拥有少量的类,同时保持每个类的职责数量小且定义明确。

“适当的” MVVM至少假设一个中等复杂的应用程序,该应用程序处理从“某处”获取的数据。它可以从数据库,文件,Web服务或无数其他来源获取数据。

在我们的例子中,我们有2类ViewModel,但没有ViewModel。该Model包装了一个CSV文件,它在启动时读取并保存在应用程序关闭时,所有的数据变化作出的用户。的View是一个窗口类,显示从所述数据Model表中的并让用户编辑的数据。csv内容可能看起来像这样:

ID, Name, Price
1, Stick, 5$
2, Big Box, 10$
3, Wheel, 20$
4, Bottle, 3$

新要求:以欧元显示价格

现在,我们被要求对我们的应用程序进行更改。数据由一个二维网格组成,该网格已经具有“价格”列,其中包含美元价格。我们需要添加一个新列,该列显示基于预定义汇率的欧元价格以及美元价格。csv文件的格式不得更改,因为其他应用程序可以使用同一文件,并且这些其他应用程序不受我们的控制。

一个可能的解决方案是将新列简单地添加到Model类中。这不是最好的解决方案,因为Model将所有公开的数据保存到CSV中-我们不希望在CSV中添加新的欧元价格列。因此,对的更改Model将是不平凡的,而且描述Model类所做的事情也将变得更加困难,这就是代码的味道

我们也可以在中进行更改View,但是当前应用程序使用数据绑定直接显示Model类提供的数据。由于我们的GUI框架不允许我们在表绑定到数据源的数据时在表中引入额外的计算列,因此我们需要对进行重大更改View以使其工作,从而使工作View更加复杂。

介绍ViewModel

ViewModel该应用程序中没有任何内容,因为到目前为止Model,它以Csv所需的确切方式呈现数据,这也是View所需的方式。有一个ViewModel无目的之间就已经增加了复杂性。但是,既然Model不再以所需的方式显示数据View,我们编写一个ViewModelViewModel项目的数据Model以这样的方式使得View可以很简单。之前,View班级已订阅该Model班级。现在,新ViewModel类订阅Model该类,并将Model的数据公开给View-,并有一个额外的列以欧元显示价格。将View不再知道Model,它现在只知道ViewModel,从View外观上看与Model以前相同-除了公开的数据包含一个新的只读列。

新要求:以不同方式格式化数据

客户的下一个要求是我们不应该将数据显示为表格中的行,而应以卡/盒的形式显示每个项目的信息(即行),并在屏幕上以4x5网格显示20个盒,显示20一次装箱。因为我们保持View简单的逻辑,所以我们只View用一个新的类来完全替换它,该类可以满足客户的需求。当然,还有另一个客户喜欢旧的客户View,因此我们现在需要同时支持这两个客户。因为所有常见的业务逻辑都已经碰巧存在,所以ViewModel这不是什么大问题。因此,我们可以通过将View类重命名为TableView并编写一个新的CardView以卡格式显示数据的类。我们还必须编写一些粘合代码,这可能是启动功能中的一个功能。

新要求:动态汇率

下一个客户要求是我们从互联网上获取汇率,而不是使用预定义的汇率。在这一点上,我们将重新讨论我之前有关“层”的声明。我们不会更改Model班级以提供汇率。相反,我们编写(或找到)一个提供汇率的完全独立的附加类。这个新类成为模型层的一部分,我们ViewModel合并了csv-Model和exchange-rate-Model的信息,然后将它们提供给View。对于此更改,甚至不必触摸旧的Model类和View类。好吧,我们确实需要将Model类重命名为,CsvModel然后将其称为new类ExchangeRateModel

如果我们当时没有引入ViewModel,而是等到现在才这样做,那么现在引入ViewModel的工作量会更多,因为我们需要从ViewandModel和move两者中删除大量功能。的功能ViewModel

单元测试后记

MVVM的主要目的不是将Model和ViewModel中的代码置于单元测试下。MVVM的主要目的是将代码分解为具有少量明确定义的职责的类。由包含少量明确定义职责的类组成的代码的几种好处之一是,将代码置于单元测试中更加容易。更大的好处是代码更易于理解,维护和修改。


3
@Peter Brilliant解释!
Mohit Shah

2
这是一篇非常出色的文章!很有帮助!
Silly Dude

嘿@Peter,炉排解释。我有一个问题。对于上面已经提到的“新要求:以欧元显示价格”的情况,您能否解释一下(也许是代码片段之类的东西)以了解viewmodel如何处理此问题。就像创建新数据模型类一样,将新列作为属性并将其用于绑定到视图吗?
斯瓦米

@swamy这部分几乎完全取决于您使用的框架。通常,新ViewModel类向预先存在的View类提供某种类型的List / Collection / Table ,并使用预先存在的Model类中的数据填充其中的大多数。并根据框架,使用填充一个list-item-property / table-column price * exchangeRate
彼得

31

实施模式和遵循最佳实践通常感觉是毫无意义的追求,但是当老板要求您添加或调整功能几个月后,您将成为转换者。使用MVVM(通常是模式),实际上您将能够遵循自己的代码,并在最坏的几小时或几天内(而不是几周或几个月)满足要求。(此更改可能只是几行代码,而不是花费数周的时间来弄清楚您最初是如何做的,甚至尝试添加新功能。)

跟进:模式和最佳实践实际上会减慢最初的开发速度,这通常给管理和工程界都很难。回报(以商务术语表示的ROI)来自具有实际上可维护,可扩展和可扩展的结构良好的代码。

例如,如果您正确地遵循MVVM,则应该能够对显示逻辑进行非常大的更改,例如换出整个视图,而不会对数据和业务逻辑产生影响。

关于为模型使用数据集的想法:(实际上我也很喜欢。)数据集似乎是在应用程序中移动模型数据的一种完全有效的方法。问题在于如何识别数据项。因为您的数据存储在行和列中,所以您必须按列名或索引执行查找,还必须针对特定行进行过滤。这些逻辑位意味着您必须在应用程序的布线逻辑中使用魔术字符串和数字。使用类型化的数据集可以缓解某些问题,但不能完全缓解。使用类型化的数据集,您将远离MVVM,而进入UI和数据源之间的紧密耦合。


1
那就是为什么我问这个问题。以前发生在我身上。我只需要一些动机来停止懒惰并写一些额外的代码(我认为重复的代码)
Michal Ciechan 2010年

您能否举一个功能的真实示例,该功能需要很长时间才能实现,使用MVVM会很容易。
Michal Ciechan

例如,您可以通过将实际的Enum值转换为用户友好的字符串(甚至本地化)来使Enum值在UI中显得更加用户友好。还是您希望用户读取“ VeryHigh”,“ LightRed”,...等值?
gehho

3
谢谢,我现在决定使用MVVM。Jason Dollinger在解释和演示MVVM方面做得最好。
Michal Ciechan 2010年

@michal Ciechan MVVM简化的现实生活功能是您的老板希望完整地重写UI。在这种情况下,您只需要编写新的UI布局XAML代码,就根本不用触摸模型。在老式的Winforms应用中,您很难同时支持两个UI。

14

它可以帮助您分离GUI和程序逻辑。混合它们会导致很难维护应用程序,尤其是当您的项目随着时间增长时。


1
+1我维护了执行100%XAML数据绑定的项目。gammelgul所说的分离将极大地帮助。
洋葱骑士

6

这里

作为开发人员,您为什么还要关心Model-View-ViewModel模式?这种模式为WPF和Silverlight开发带来许多好处。在继续之前,请问自己:

  • 您是否需要与设计师共享项目,并具有使设计工作和开发工作几乎同时进行的灵活性?
  • 您是否需要对解决方案进行彻底的单元测试?
  • 在组织中的项目内部和之间具有可重用的组件对您来说重要吗?
  • 您是否需要更大的灵活性来更改用户界面,而不必重构代码库中的其他逻辑?

如果您对这些问题中的任何一个回答“是”,那么这些只是使用MVVM模型可以为您的项目带来的一些好处。


5
  • 与设计师(不是程序员,而是使用Blend的人)一起工作更容易
  • 代码是可测试的(单元测试)
  • 在不弄乱其余代码的情况下更改视图要容易得多
  • 在开发UI时,您可以在不运行真实服务的情况下(仅使用模型中的模拟数据)模拟模型和开发接口。然后,您只需翻转标志并连接到服务。

4

摘自Josh Smith关于MVVM的文章

除了WPF(和Silverlight 2)功能使MVVM成为构建应用程序的自然方式之外,该模式也很流行,因为ViewModel类易于进行单元测试。当应用程序的交互逻辑位于一组ViewModel类中时,您可以轻松编写测试它的代码。从某种意义上说,视图和单元测试只是两种不同类型的ViewModel使用者。拥有针对应用程序的ViewModels的测试套件可提供免费和快速的回​​归测试,这有助于降低随着时间的推移维护应用程序的成本。

对我来说,这是使用MVVM的最重要原因。

以前,我有将视图和视图模型混在一起的控件。但是视图本质上将鼠标和键盘事件作为输入,并将绘制的像素作为输出。您如何对这样的内容进行单元测试?MVVM消除了无法测试的视图与可测试的视图模型的距离,并使该视图层尽可能地薄,从而解决了该问题。


3

关于MVVM有很多好处,但是也许最重要的是测试代码的能力(对ViewModels进行单元测试)。

视图和视图模型之间缺乏连接确实也有助于松散耦合。重用您编写的组件变得非常容易。


很公平。测试它会更容易.....或更有意义。就我个人而言,我无法想到我无法在
DataView

2
非常简单的示例:您可以具有按钮的启用状态的属性,然后将按钮的IsEnabled属性绑定到ViewModel中的该属性。现在,您可以单元测试启用/禁用按钮的逻辑是否正确。告诉我如何在数据集中执行此操作。;)
gehho 2010年

3

阅读本文中的MVVM简介

2005年,John Gossman(目前是Microsoft的WPF和Silverlight架构师之一)在他的博客中公开了Model-View-ViewModel(MVVM)模式。MVVM与Fowler的Presentation Model相同,因为这两种模式都具有View的抽象,其中包含View的状态和行为。Fowler引入了Presentation Model作为创建独立于UI平台的View抽象的一种方式,而Gossman引入了MVVM作为利用WPF核心功能简化用户界面创建的标准化方法。从这个意义上讲,我认为MVVM是针对WPF和Silverlight平台量身定制的更为通用的PM模式的专业化。

..

与MVP中的Presenter不同,ViewModel不需要引用视图。视图绑定到ViewModel的属性,而ViewModel的属性又公开了模型对象中包含的数据以及该视图特定的其他状态。view和ViewModel之间的绑定很容易构造,因为ViewModel对象被设置为视图的DataContext。如果ViewModel中的属性值发生更改,这些新值将通过数据绑定自动传播到视图。当用户单击视图中的按钮时,将执行ViewModel上的命令以执行请求的操作。ViewModel(而不是View)执行对模型数据所做的所有修改。视图类不知道模型类存在,而ViewModel和模型不知道视图。实际上,该模型完全忽略了ViewModel和视图的存在。

文章还解释了为什么使用这些gui模式:

在简单的“ Hello,World!”中使用设计模式是不必要且适得其反的。程序。任何有能力的开发人员一眼就能理解几行代码。但是,随着程序中功能数量的增加,代码行和移动部件的数量也相应增加。最终,系统的复杂性及其所包含的反复出现的问题,促使开发人员以一种易于理解,讨论,扩展和排除故障的方式来组织代码。通过将众所周知的名称应用于源代码中的某些实体,我们减少了复杂系统的认知混乱。我们通过考虑代码在系统中的功能角色来确定要应用于一段代码的名称。

开发人员经常有意根据设计模式来构造代码,而不是让这些模式有机地出现。两种方法都没有错,但是在本文中,我研究了显式使用MVVM作为WPF应用程序体系结构的好处。某些类的名称包含MVVM模式中的知名术语,例如,如果该类是视图的抽象,则以“ ViewModel”结尾。这种方法有助于避免前面提到的认知混乱。相反,您可以快乐地处于受控混乱的状态,这是大多数专业软件开发项目中的自然状态!


3

我本人仍将继续熟悉该模式,但我确实认为它很有价值。当前最大的挑战是该方法仍然很新,因此存在很多混乱,并且该模式的某些关键组件仍然难以实现。我发现了一些有助于我更清晰地实现该模式的东西:

  1. 我大量使用了Josh Smith的MVVM Foundation的RelayCommand 。这使得通过命令从View到ViewModel的绑定更加简洁。

  2. 我使用AOP来减轻实现INotifyPropertyChanged的痛苦。我目前正在使用Postsharp,尽管我相信还有其他工具可以做到这一点。如果我还没有发现这一点,那么我现在可能会放弃,因为手动实现它的样板代码确实让我很烦。

  3. 我不得不将我的方法转换为软件实现方式。我的软件不再是一个独裁者类,该独裁者类告诉其所有奴才做什么,反过来又使用他们的奴才,而我的软件则更多地是由松散耦合的服务决定的:

    • 这就是我所知道的

    • 这些是我需要做的事情

当您开始以这种方式构建代码并使用易于连接依赖项的工具(有多种IoC框架可供选择)时,我发现它减轻了MVVM的某些尴尬,例如您可以减少与将模型注入ViewModel并查找供ViewModel使用的各种View Services(例如显示文件对话框)相关的样板代码。

学习这种不同的方法是一笔巨大的投资,并且与实施中的任何重大变化一样,当您首次开始使用它时,生产率会大大降低。但是,我开始在隧道尽头看到一些曙光,我相信,一旦掌握了凌乱的细节,我的应用程序将变得更干净,更可维护。


为了通过Postsharp解决有关INotifyPropertyChanged的问题,我基于此处的示例使用了Aspect 。我对它进行了一些自定义,以供使用,但这可以使您明白要点。这样,我只需标记类[NotifyPropertyChanged],所有公共属性都将在其设置器中实现模式(即使它们是自动属性设置器)。对我来说,这很干净,因为我不必担心是否要花时间使该类实现INotifyPropertyChanged。我可以添加属性并完成它。


请进一步说明如何使用PostSharp实施INotifyPropertyChanged。我只是将OnPropertyChanged放在我的基本ViewModel类中,但还没有发现它很麻烦。到目前为止,我仅使用PostSharp来跟踪方法调用。您如何使用它?
罗伯特·罗斯尼

2

我同意使用MVVM通过编写矿石代码将更多的精力放在我们的肩膀上,但是要看好所有孤立的方面,如果您是设计师,则可以设计程序,其他人可以为您编写代码,数据库也可以这样做层,如果您不使用MVVM,请查看您在多企业环境中的可维护性,尤其是在大型企业应用程序中,那么维护几乎是致命的……。我本人在开发ERP解决方案时就使用了它,因为维护非常简单该隔离级别


1

从长远来看,如果由于其他原因而使用MVVM之类的模式,您将很高兴。请记住,您无需一字接一句地遵循模式要求,只需确保在窗口(视图)和逻辑(后置代码)之间有良好的分隔即可。


0

MVVM的好处

  1. 降低复杂性。
  2. 设计与开发的隔离。
  3. 依赖注入。
  4. 主要优点是,当您拥有结构良好的MVVM Windows Phone应用程序并希望为Windows Metro Desktop开发该应用程序时,您只想集中精力设计,因为可以使用相同的视图模型。

希望能帮助到你。


0

MVVM确实是过多的代码。

那么,MVVM有什么好处?

仅仅是关注分离而已。您也可以将ViewModel逻辑写入Controller。ViewModel仅负责进行转换(例如,将对象转换为字符串)。通过使用MVVM,您可以使用更多的面向对象的编程风格。通过将转换逻辑写入控制器,您可以使用更多功能的编程样式。

因此,归结为拥有更多具有更好可读性的代码或拥有大型Controller文件的更少代码。总之,您不能说必须使用MVVM,因为它比MVC更好,这只是个人喜好。

为了清楚起见,为什么我提到控制器:MVVM在某处也有控制器代码。我不知道为什么离开C会达成广泛共识。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.