为什么要使用 MVVM? [英] Why use MVVM?

查看:85
本文介绍了为什么要使用 MVVM?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好吧,我一直在研究 MVVM 模式,每次我之前尝试研究它时,我都出于多种原因放弃了:

  1. 不必要的超长卷编码
  2. 对于程序员来说没有明显的优势(我的办公室里没有设计师.目前只有我自己很快就会成为另一个程序员)
  3. 关于良好做法的资源/文档不多!(或者至少很难找到)
  4. 想不出哪一种情况是有利的.

我又要放弃了,想问问有没有人回答上面的原因.

老实说,我看不出将此用于单个/合作伙伴编码的优势.即使在具有 10 个窗口的复杂项目中.对我来说,数据集是一个足够好的视图和绑定,就像 Brent问题

有人可以举例说明与 XAML 数据绑定相比,使用 MVVM 模式在哪些方面可以节省时间.

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


在花了一个下午的时间研究了 MVVM 之后,我终于找到了一些让我意识到它的真正好处的东西 答案.

解决方案

总结

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

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

对于足够简单的应用程序来说,每个设计模式都是多余的.假设您编写了一个 GUI 应用程序,该应用程序显示一个按钮,按下该按钮时会显示Hello world".在这种情况下,MVC、MVP、MVVM 等设计模式都增加了很多复杂性,而没有增加任何价值.

一般来说,仅仅因为某种设计模式有点合适就引入它总是一个错误的决定.应该使用设计模式来降低复杂性,要​​么直接降低整体复杂性,要​​么用熟悉的复杂性替换不熟悉的复杂性.如果设计模式不能通过这两种方式中的任何一种来降低复杂性,请不要使用它.

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

  • "D.€|Ré%dfà?c"
  • 正确的HorseBatteryStaple"

虽然第二个字符序列的长度是第一个序列的两倍,但它比第一个序列更容易阅读、更快编写和更容易记住,这一切都是因为它更熟悉.对于熟悉的代码模式也是如此.

当您考虑到熟悉程度取决于读者时,这个问题就获得了另一个维度.一些读者会发现3.14159265358979323846264338327950"比上述任何一个密码都更容易记住.有些不会.因此,如果您想使用 MVVM 的一种风格,请尝试使用在您使用的特定语言和框架中反映其最常见形式的一种.

MVVM

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

'Proper' MVVM 假设至少是一个中等复杂的应用程序,它处理它从某处"获取的数据.它可以从数据库、文件、Web 服务或无数其他来源获取数据.

示例

在我们的示例中,我们有 2 个类 ViewModel,但没有 ViewModel.Model 包装了一个 csv 文件,它在启动时读取并在应用程序关闭时保存,其中包含用户对数据所做的所有更改.View 是一个 Window 类,它在表格中显示来自 Model 的数据并让用户编辑数据.csv 内容可能看起来像这样:

ID、名称、价格1, 棒, 5$2,大盒子,10$3、轮子,20$4, 瓶子, 3$

新要求:以欧元显示价格

现在我们被要求对我们的应用程序进行更改.数据由一个二维网格组成,该网格已经有一个价格"列,包含以美元为单位的价格.我们需要添加一个新列,根据预定义的汇率显示除美元以外的欧元价格.csv 文件的格式不得更改,因为其他应用程序使用相同的文件,而这些其他应用程序不受我们的控制.

一个可能的解决方案是简单地将新列添加到 Model 类.这不是最好的解决方案,因为 Model 保存了它向 csv 公开的所有数据 - 我们不希望在 csv 中出现新的欧元价格列.因此,对 Model 的更改将是非常重要的,并且也很难描述 Model 类的作用,即 代码异味.

我们也可以在 View 中进行更改,但是我们当前的应用程序使用数据绑定来直接显示由我们的 Model 类提供的数据.因为当表是绑定到数据源的数据时,我们的 GUI 框架不允许我们在表中引入额外的计算列,所以我们需要对 View 进行重大更改以实现这一点工作,使 View 变得更加复杂.

介绍视图模型

应用程序中没有 ViewModel 因为直到现在 Model 以完全符合 Csv 需要的方式呈现数据,这也是 查看需要它.中间有一个 ViewModel 会毫无目的地增加复杂性.但是现在 Model 不再以 View 需要的方式呈现数据,我们编写了一个 ViewModel.ViewModelView 可以简单的方式投影Model 的数据. 以前的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,并调用新类 ExchangeRateModel.

如果我们当时没有引入 ViewModel 而是等到现在才引入,那么现在引入 ViewModel 的工作量会更高,因为我们需要从两个ViewModel 并将功能移动到 ViewModel 中.

单元测试后记

MVVM 的主要目的不是让 Model 和 ViewModel 中的代码可以进行单元测试.MVVM 的主要目的是将代码分解为具有少量明确职责的类.代码由具有少量明确职责的类组成的几个好处之一是可以更容易地将代码置于单元测试之下.更大的好处是代码更易于理解、维护和修改.

Okay, I have been looking into MVVM pattern, and each time I have previously tried looking into it, I gave up for a number of reasons:

  1. Unnecessary Extra Long Winded Coding
  2. No apparent advantages for coders (no designers in my office. Currently only myself soon to be another coder)
  3. Not a lot of resources/documentation of good practices! (Or at least hard to find)
  4. Cannot think of a single scenario where this is advantageous.

I'm about to give up on it yet again, and thought I'd ask to see if someone answer the reasons above.

I honestly can't see an advantage of using this for a single/partner coding. Even in complex projects with 10's of windows. To me the DataSet is a good enough view and binding like in the answer by Brent following question

Could someone show an example of where using MVVM pattern would of saved time when compared to XAML DataBinding.

100% of my binding is done in XAML at the moment. And therefore I don't see the point of the VM as its just extra code behind that I need to write and depend on.

EDIT:
After spending the afternoon researching about MVVM I have finally found something that made me realise the true benefits of it from this answer.

解决方案

Summary

  • The usage of all patterns is situational, and the benefit (if there is any) always lies in reduced complexity.
  • MVVM guides us how to distribute responsibilities between classes in a GUI application.
  • ViewModel projects the data from the Model into a format that fits the View.
  • For trivial projects MVVM is unnecessary. Using only the View is sufficient.
  • For simple projects, the ViewModel/Model split may be unnecessary, and just using a Model and a View is good enough.
  • Model and ViewModel do not need to exist from the start and can be introduced when they are needed.

When to use patterns and when to avoid them

For a sufficiently simple application every design pattern is overkill. Assume you write a GUI application that displays a single button which when pressed shows "Hello world". In this case, design patterns like MVC, MVP, MVVM all add a lot of complexity, while not adding any value whatsoever.

In general, it is always a bad decision to introduce a design pattern just because it somewhat fits. Design patterns should be used to reduce complexity, either by directly reducing overall complexity, or by replacing unfamiliar complexity with familiar complexity. If the design pattern cannot reduce complexity in either of these 2 ways, do not use it.

To explain familiar and unfamiliar complexity, take the following 2 sequences of characters:

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

While the second character sequence is twice the length of the first sequence, it's easier to read, faster to write, and easier to remember than the first sequence, all because it's more familiar. The same holds true for familiar patterns in code.

This problem gains another dimension when you consider that familiarity depends on the reader. Some readers will find "3.14159265358979323846264338327950" easier to remember than either of the above passwords. Some won't. So if you want to use a flavor of MVVM, try to use one that mirrors its most common form in the specific language and framework you're using.

MVVM

That said, let's dive into the topic of MVVM by means of an example. MVVM guides us how to distribute responsibilities between classes in a GUI application (or between layers - more about this later), with the goal of having a small number of classes, while keeping the number of responsibilities per class small and well defined.

'Proper' MVVM assumes at least a moderately complex application, which deals with data it gets from "somewhere". It may get the data from a database, a file, a web service, or from a myriad of other sources.

Example

In our example, we have 2 classes View and Model, but no ViewModel. The Model wraps a csv-file which it reads on startup and saves when the application shuts down, with all changes the user made to the data. The View is a Window class that displays the data from the Model in a table and lets the user edit the data. The csv content might look somewhat like this:

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

New Requirements: Show price in Euro

Now we are asked to make a change to our application. The data consists of a 2-dimensional grid which already has a "price" column, containing a price in USD. We need to add a new column which shows prices in Euro in addition to those in USD, based on a predefined exchange rate. The format of the csv-file must not change because other applications work with the same file, and these other applications are not under our control.

A possible solution is to simply add the new column to the Model class. This isn't the best solution, because the Model saves all the data it exposes to the csv - and we do not want a new Euro price column in the csv. So the change to the Model would be non-trivial, and it would also be harder to describe what the Model class does, which is a code smell.

We could also make the change in the View, but our current application uses data binding to display the data directly as provided by our Model class. Because our GUI framework doesn't allow us to introduce an additional calculated column in a table when the table is data bound to a data source, we would need to make a significant change to the View to make this work, making the View a lot more complex.

Introducing the ViewModel

There is no ViewModel in the application because until now the Model presents the data in exactly the way the Csv needs it, which is also the way the View needed it. Having a ViewModel between would have been added complexity without purpose. But now that the Model no longer presents the data in the way the View needs it, we write a ViewModel. The ViewModel projects the data of the Model in such a way that the View can be simple. Previously the View class subscribed to the Model class. Now the new ViewModel class subscribes to the Model class, and exposes the Model's data to the View - with an extra column displaying the price in Euros. The View no longer knows the Model, it now only knows the ViewModel, which from the point of the View looks the same as the Model did before - except that the exposed data contains a new read only column.

New requirements: different way to format the data

The next customer request is that we should not display the data as rows in a table, but instead display the information of each item (a.k.a. row) as a card/box, and display 20 boxes on the screen in a 4x5 grid, showing 20 boxes at a time. Because we kept the logic of the View simple, we simply replace the View entirely with a new class that does as the customer desires. Of course there is another customer who preferred the old View, so we now need to support both. Because all of the common business logic already happens to be in the ViewModel that is not much of an issue. So we can solve this by renaming the View class into TableView, and writing a new CardView class that shows the data in a card format. We will also have to write some glue code, which might be a oneliner in the startup function.

New requirements: dynamic exchange rate

The next customer request is that we pull the exchange rate from the internet, rather than using a predefined exchange rate. This is the point where we revisit my earlier statement about "layers". We don't change our Model class to provide an exchange rate. Instead we write (or find) a completely independent additional class that provides the exchange rate. That new class becomes part of the model layer, and our ViewModel consolidates the information of the csv-Model and the exchange-rate-Model, which it then presents to the View. For this change the old Model class and the View class do not even have to be touched. Well, we do need to rename the Model class to CsvModel and we call the new class ExchangeRateModel.

If we hadn't introduced the ViewModel when we did but had instead waited until now to do so, the amount of work to introduce the ViewModel now would be higher because we need to remove significant amounts of functionality from both of the View and the Model and move the functionality into the ViewModel.

Afterword on Unit Tests

The primary purpose of MVVM is not that the code in the Model and the ViewModel can be put under Unit Test. The primary purpose of MVVM is that the code is broken up into classes with a small number of well defined responsibilities. One of several benefits of having code consisting of classes with a small number of well defined responsibilities is that it is easier to put the code under Unit Test. A much larger benefit is that the code is easier to understand, maintain, and modify.

这篇关于为什么要使用 MVVM?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆