首页 文章

MVVM是否违反DRY?

提问于
浏览
39

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

  • SmartForm :表示要填写的数据表单的模型具有以下属性:

  • IdCode

  • Headers

  • 说明

  • SmartFormFields的集合

  • SmartFormControlView 查看

  • SmartFormControlViewModel ViewModel

  • IdCode

  • Headers

  • 说明

  • SmartFormFields的集合

所以我的 ViewModel is basically the same as my Model ,只是具有与View绑定的所有OnPropertyChanged功能 .

似乎我重构并扩展了我对我的模型所做的每一点改变,我都是这样做的 . 我是 have to make a mirror change to the ViewModel .

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

Am I implementing the MVVM pattern incorrectly or is it just an inherent characteristic of MVVM that there is always a 1-to-1 repetition going on between Model and ViewModel?

8 回答

  • 3

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

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

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

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

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

  • 22

    一个简单的解决方案是使用一个抽象的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进行更改的情况,并且当它执行任何绑定到该属性的控件时,将告知发生了某些事情 .

    希望有所帮助 .

  • 1

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

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

  • 1

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

  • 0

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

    通常,您的数据层,业务层和UI层之间会有某种重复 . 毕竟,通常您必须向最终用户(UI)显示每个属性,为其行为建模(业务层)并保留值(数据层) .

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

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

    2012 Update

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

  • 1

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

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

    考虑到刚才所说的,我在ViewModel中使用相同的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; }
        }
    
  • 0

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

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

    从本质上讲 - 您对MVVM(现代的MV-Variant of MVC)的建模/理解可能有些不对劲 .


    编辑:

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

  • 1

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

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

相关问题