MVVM是否违反DRY?

时间:2009-06-08 08:16:57

标签: wpf design-patterns mvvm

我制作的ViewModels看起来像其他类一样可疑,并且它们似乎需要大量的代码重复,例如在我目前的项目中:

  • SmartForm 模型表示要填写的数据表单,具有以下属性:
    • IDCODE
    • 标题
    • 描述
    • SmartFormFields的集合
  • SmartFormControlView 查看
  • SmartFormControlViewModel ViewModel
    • IDCODE
    • 标题
    • 描述
    • SmartFormFields的集合

所以我的 ViewModel与我的Model 基本相同,只是所有OnPropertyChanged功能都与View绑定。

似乎我重构并扩展了我对模型所做的每一点改变,我必须对ViewModel进行 镜像更改

这似乎违反了模式的基本规则不要重复自己

我是否错误地实现了MVVM模式,或者只是MVVM的固有特性,模型和ViewModel之间始终存在一对一的重复?

8 个答案:

答案 0 :(得分:23)

我个人认为它不会违反DRY,因为模型和视图模型(我更喜欢术语演示者)并不指向相同的信息。例如,您的VM和M都具有Title属性,但您的VM的Title属性也可以包含验证,而您的模型的Title属性可以假定有效。

虽然虚拟机可能包含模型的所有属性,但也有可能进行验证(例如,标题必须是非空白的),数据依赖性,可绑定的UI特定属性(图标,颜色) ,画笔等)不属于视图的一部分。

基本上所有UI模式在您声明的方式中都有类似的“重复”:即级联修改。尝试在不更改控制器的情况下更改MVC中的模型。

话虽这么说,MVVM(或任何用于分离UI,逻辑和状态的UI模式)对于简单的情况(例如您的示例)来说可能过于繁琐。当逻辑变得比状态传递更多时,分隔控制器/演示者/视图模型的值会减少。

在您的特定情况下,如果您的虚拟机没有任何逻辑,验证或UI特定属性,并且您的模型不必持久化,序列化或向后兼容现有结构(或在VM中添加逻辑这样做很简单),我强烈考虑将M和VM结合起来,以避免创建其唯一目的是获取/设置基础模型属性的属性。

答案 1 :(得分:3)

一个简单的解决方案是使用一个抽象的ViewModel(VM)基类来公开模型。您可以在有意义的情况下选择此VM。

public abstract class ViewModelBase<T>
{
    public T Model { get; set; }
}

如果您的模型已实现INotifyPropertyChanged,您的视图将获得该事件。这样做是为了让您的View访问模型中的每个属性,而这些属性并不是您想要的。

你也可以利用这样的属性初始化器(我个人已经存储在代码片段中):

public abstract class SampleViewModel
{
    public int MyProperty
    {
        get { return Model.MyProperty; }
        set
        {
            Model.MyProperty = value;
            OnPropertyChanged("MyProperty");
        }
    }
}

在大多数情况下,您将查看将对您的VM进行更改的情况,以及何时对该属性进行绑定的任何控件都将被告知发生了某些事情。

希望有所帮助。

答案 2 :(得分:1)

这是一个有趣的评论......实际上,通常需要修改ViewModel以反映模型中的变化。

如果它可以是自动的那就太好了......实际上我认为通过在ViewModel中实现ICustomTypeDescriptor是可能的:GetProperties将通过反射返回模型的所有属性。但是我不确定它是否有意义,因为模型可能根本不包含属性:它可能是方法,字段或任何东西,并且模型中的所有内容都不会在ViewModel中有用。

答案 3 :(得分:1)

其他人对MVC / MVVM模式的组件角色提供了很好的评论。无论你选择哪种模式,我都想提供一个解释重复性的基本观察。

通常,您的数据层,业务层和UI层之间会有某种重复。毕竟,通常你必须显示每个属性给最终用户(UI),模型它的行为(业务层)和持久化值(数据层)。

正如其他人所指出的那样,每一层的属性可能会有所不同,这解释了一些重复的基本需求。

当处理足够大的系统(或使用合适团队的小型项目)时,我倾向于使用UML对此类信息进行建模,并使用代码生成(通常与部分类相结合)来处理重复方面。举个简单的例子,Last Name属性可能要求(在我的UML模型中)它将数据限制为50个字符。我可以生成代码以在我的UI层中强制执行该限制(例如通过物理限制输入),在我的业务层中生成代码以重新检查该限制(“永远不信任UI”),如果数据太长则可能抛出异常,并生成我的持久层(例如NVARCHAR(50)列,相应的ORM映射文件等)。

2012年更新

Microsoft的Data Annotations及其在UI层(例如ASP.Net MVC)和数据层(Entity Framework)的支持在实现我之前生成的许多问题方面走了很长的路对

答案 4 :(得分:1)

这里似乎已经遗漏了一件事,并且您的简单示例未公开,您的视图通常会聚合多个域模型类型中包含的数据。在这种情况下,您的ViewModels将包含对许多域模型(具有不同类型)的引用,从而聚合特定视图可能希望公开的一组相关数据。

答案 5 :(得分:1)

Eric Evans在他的“领域驱动设计”一书中提到模型重构不应该太难,并且概念更改不应该跨越太多模块,否则,重构模型会变得过高,所以,如果你问的话我,在ViewModel中使用模型类型'复制'肯定很难进行模型重构。

Eric提到,应该更加重视模型的内聚和隔离,而不是基于技术问题(数据库访问,POCOS,演示)的层次划分中的整洁。最重要的问题是域模型是业务域的良好表示,因此它对于域模型在单个隔离层中最重要,而不是跨越多个模块,以便它很容易更新(重构)。

考虑到刚才所说的,我在ViewModel中使用相同的Model对象,如果我想降低对Model对象的“访问”级别,那么我将“传递”对接口的引用由Model对象实现。例如:

    // The primary Model classes
    public partial class OrderItem {
        public int Id { get; }
        public int Quantity { get; set; }
        public Product Item { get; set; }
        public int Total { get; set; }
        public void ApplyDiscount(Coupon coupon) {
            // implementation here
        }
    }

    public partial class Product {
        public int Id { get; }
        public string Name { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }    
    }

    // The shared views of those model classes
    public partial class OrderItem : IOrderItemDTO {
        public IProductDTO Item { 
            get { 
                return this.product; 
            } 
        }
    }

    public partial class Product : IProductDTO {
    }


   // Separate interfaces...
   // You enforce the rules about how the model can be
   // used in the View-ViewModel, without having to rewrite
   // all the setters and getters.
    public interface IOrderItemDTO {
        int Id { get; }
        int Quantity { get; set; }
        IProductDTO Item { get; }
        int Total { get; }
    }

    public interface IProductDTO {
        string Name { get; }
        string Description { get; }
        decimal Price { get; }
    }

    // The viewmodel...
    public class OrderItemViewModel {
        IOrderItemDTO Model { get; set; }
    }

答案 6 :(得分:0)

我只知道MVC,而在MVC中,包含GUI的Model-Class有些错误。 SmartForm似乎是一个Form,这意味着它不是一个模型。我不知道你要编程的是什么,但我给你一个模型的例子:

拿一份日历。您可以询问班级今天的日期,月份,每月有多少天,...... 它没有图形表示。视图(您想要的CalenderViewMonth或whater)在屏幕上打印一个月。它知道一个日历并询问他在不同的单元格中写什么。

基本上 - 你在建模/理解MVVM(这是一个现代的MV-Variant of MVC)时可能会出错。


编辑:

我只是在维基百科上查找了MVVM。模型就像MVC中的模型一样。像MVC中的View一样查看 - 仅图形表示。 ViewModel是通用View和专用Model之间的Glue。某种适配器。不应该违反DRY。

答案 7 :(得分:0)

我认为是的,vanilla MVVM确实违反了DRY。但我已经开始the PDX library,我认为在很多情况下我可以解决这个问题。我写了this post我相信这个问题。

基本上,我的目标(其中之一)是让ViewModel不用担心UI通知。 PDX项目仍处于起步阶段,但如果您正在阅读此问题,您可能会发现它很有用,我将非常感谢您提供的任何反馈。