62

在使用 MVVM 模式做了几个项目之后,我仍然在为 ViewModel 的角色苦苦挣扎:

我过去所做的:仅将模型用作数据容器。将逻辑用于操作 ViewModel 中的数据。(那是业务逻辑对吗?) 缺点:逻辑不可重用。

我现在正在尝试:保持 ViewModel 尽可能薄。将所有逻辑移入模型层。仅在 ViewModel 中保留演示逻辑。缺点:如果模型层内的数据发生更改,则 UI 通知会非常痛苦。

所以我会给你一个例子,让它更清楚:

场景:重命名文件的工具。类: File :代表每个文件;规则:包含如何重命名文件的逻辑;

如果我遵循方法 1:为文件、规则和视图创建 ViewModel -> RenamerViewModel。将所有逻辑放入 RenamerViewModel:包含 FileViewModel 和 RuleViewModel 的列表以及进行中的逻辑。简单快捷,但不可重复使用。

如果我遵循方法 2:创建一个新的模型类 - > 重命名器,其中包含文件列表、规则和进行中的逻辑,以遍历每个文件并应用每个规则。为文件、规则和重命名器创建视图模型。现在 RenamerViewModel 只包含一个 Renamer Model 的实例,外加两个 ObservableCollections 来包装 Renamer 的 File und Rule List。但是整个逻辑都在重命名模型中。因此,如果 Renamer Model 被触发以通过方法调用来操作某些数据,则 ViewModel 不知道操作了哪些数据。因为模型不包含任何 PropertyChange 通知,我会避免这种情况。所以业务和表示逻辑是分开的,但这使得通知 UI 变得很困难。

4

4 回答 4

67

将业务逻辑放在视图模型中是一种非常糟糕的做事方式,所以我要快速说永远不要这样做,然后继续讨论第二个选项。

将逻辑放入模型中更加合理,这是一个很好的开始方法。有什么缺点?你的问题说

因此,如果 Renamer Model 被触发以通过方法调用来操作某些数据,则 ViewModel 不知道操作了哪些数据。因为模型不包含任何 PropertyChange 通知,我会避免这种情况。

好吧,实现你的模型INotifyPropertyChanged肯定会让你继续做更好的事情。但是,确实有时无法做到这一点——例如,模型可能是一个部分类,其中属性由工具自动生成并且不会引发更改通知。这很不幸,但不是世界末日。

如果你想买东西,就得有人付钱;如果不是提供此类通知的模型,那么您只有两个选择:

  1. viewmodel 知道模型上的哪些操作(可能)导致更改,并在每次此类操作后更新其状态。
  2. 其他人知道哪些操作会导致更改,并且他们通知视图模型在其包装更改的模型之后更新其状态。

第一个选项又是一个坏主意,因为实际上它又回到了将“业务逻辑”放在视图模型中。不如将所有业务逻辑放在视图模型中那么糟糕,但仍然如此。

第二种选择更有希望(不幸的是需要更多的工作来实现):

  • 将部分业务逻辑放在一个单独的类(“服务”)中。该服务将通过适当地使用模型实例来实现您希望执行的所有业务操作。
  • 这意味着服务知道模型属性何时可能更改(这没关系:模型 + 服务 == 业务逻辑)。
  • 该服务将向所有相关方提供有关更改模型的通知;您的视图模型将依赖于服务并接收这些通知(因此他们将知道“他们的”模型何时更新)。
  • 由于业务操作也由服务实现,这很自然(例如,当在视图模型上调用命令时,反应是调用服务上的适当方法;请记住,视图模型本身不知道业务逻辑)。

有关此类实现的更多信息,另请参阅我的答案herehere

于 2013-05-02T13:10:16.547 回答
13

两种方法都有效,但还有第三种方法:在模型层和 VM 层之间实现服务。如果你想让你的模型保持沉默,服务可以提供一个与 UI 无关的中间人,它可以以可重用的方式执行你的业务规则。

因为模型不包含任何 PropertyChange 通知,我会避免这种情况

你为什么要回避这个?不要误会我的意思,我倾向于让我的模型尽可能地愚蠢,但是在你的模型中实现更改通知有时会很有用,而且你只依赖于System.ComponentModel何时这样做。它完全与 UI 无关。

于 2013-05-02T13:10:03.867 回答
3

我执行以下操作

  1. 仅使用 XAML 视图逻辑查看

  2. ViewModel 处理点击处理程序和创建新的视图模型。处理路由事件等。

  3. 模型是我的数据容器和关于验证模型数据的业务逻辑。

  4. 用数据填充模型的服务。例如调用网络服务器,从磁盘加载,保存到磁盘等。根据示例,我的模型和服务通常都会实现 IPropertyChanged。或者他们可能有事件处理程序。

任何复杂的应用程序imo都需要另一层。我称之为模型+服务、视图、视图模型。该服务抽象您的业务逻辑并将模型实例作为依赖项或创建模型。

于 2017-08-25T13:14:26.783 回答
0

您还可以在 Model 和 ViewModel 上实现 IDataErrorInfo,但仅在 Model 中进行验证,这将简化您仅在 Model ...

前任:

视图模型:

...

private Person person;

...

string IDataErrorInfo.this[string propertyName]
{
    get
    {
        string error = (person as IDataErrorInfo)[propertyName];
        return error;
    }
}

模型:

public class Person:INotifyPropertyChanged,IDataErrorInfo
{

...

   string IDataErrorInfo.this[string propertyName]
   {
        get { return this.GetValidationError(propertyName); }
   }

...

   string GetValidationError(string propertyName)
   {
        if(propertyName == "PersonName")
             //do the validation here returning the string error
   }
}

另外看看 MVCVM 模式,我实际使用它,将业务逻辑抽象为控制器类而不是模型或视图模型非常好

于 2013-09-16T18:19:38.967 回答