似乎我制作的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 回答
我个人不认为它违反DRY,因为模型和视图模型(我更喜欢术语演示者)不指向相同的信息 . 例如,您的VM和M都具有Title属性,但您的VM的Title属性也可以包含验证,而您的模型的Title属性可以假定有效性 .
虽然虚拟机可能包含模型的所有属性,但也有可能进行验证(例如, Headers 必须是非空白的),数据依赖性,可绑定的UI特定属性(图标,颜色,画笔,等)不属于视图的一部分 .
基本上所有UI模式在您声明它的方式上都有类似的“重复”:即级联修改 . 尝试在不更改控制器的情况下更改MVC中的模型 .
话虽这么说,MVVM(或任何用于分离UI,逻辑和状态的UI模式)对于简单的情况(例如您的示例)来说可能过于繁琐 . 当逻辑变得只是状态传递时,分隔控制器/演示者/视图模型的值减小 .
在您的特定情况下,如果您的VM没有任何逻辑,验证或UI特定属性,并且您的模型不必持久化,序列化或向后兼容现有结构(或者在VM中添加逻辑这样做很简单),我强烈考虑将M和VM结合起来以避免创建其唯一目的是获取/设置基础模型属性的属性 .
一个简单的解决方案是使用一个抽象的ViewModel(VM)基类来公开模型 . 您可以在有意义的情况下选择此VM .
即
如果您的模型已实现INotifyPropertyChanged,您的视图将获得该事件 . 这样做是为了让您的View访问模型中的每个属性,而这些属性并不是您想要的 .
你也可以利用这样的属性初始化器(我个人已经存储在代码片段中):
在大多数情况下,您将查看将对您的VM进行更改的情况,并且当它执行任何绑定到该属性的控件时,将告知发生了某些事情 .
希望有所帮助 .
这是一个有趣的评论......实际上,通常需要修改ViewModel以反映模型中的更改 .
如果它可以是自动的那就太好了......实际上我认为通过在ViewModel中实现ICustomTypeDescriptor是可能的:GetProperties将通过反射返回模型的所有属性 . 但是我不确定它是否有意义,因为模型可能根本不包含属性:它可能是方法,字段或任何东西,并且模型中的所有内容都不会在ViewModel中有用 .
这里似乎已经遗漏了一件事,并且您的简单示例未公开,您的视图通常会聚合多个域模型类型中包含的数据 . 在这种情况下,您的ViewModel将包含对许多域模型(具有不同类型)的引用,从而聚合特定视图可能希望公开的一组相关数据 .
其他人对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)上的支持在实现我之前为其生成代码的许多问题方面走了很长的路 .
Eric Evans在他的“领域驱动设计”一书中提到模型重构不应该太难,并且概念改变不应该跨越太多模块,否则,重构模型会变得过高,所以,如果你问我,有在ViewModel中模型排序'复制'肯定很难进行模型重构 .
Eric提到,应该更加重视模型的内聚和隔离,而不是基于技术问题(数据库访问,POCOS,演示)的层次划分中的整洁 . 最重要的问题是域模型是业务域的良好表示,因此它对于域模型在单个隔离层中最重要,而不是跨越多个模块,以便它很容易更新(重构) .
考虑到刚才所说的,我在ViewModel中使用相同的Model对象,如果我想降低对Model对象的“访问”级别,那么我将“传递”对由模型对象 . 例如:
我只知道MVC,而在MVC中,包含GUI的Model-Class有些错误 . SmartForm似乎是一个Form,这意味着它不是一个模型 . 我不知道你要编程的是什么,但我给你一个模型的例子:
拿一份日历 . 您可以向 class 询问今天的日期,月份,每月的天数,......但它没有图形表示 . 视图(您想要的CalenderViewMonth或whater)在屏幕上打印一个月 . 它知道一个日历并询问他在不同的单元格中写什么 .
从本质上讲 - 您对MVVM(现代的MV-Variant of MVC)的建模/理解可能有些不对劲 .
编辑:
我只是在维基百科上查找了MVVM . 模型就像MVC中的模型一样 . 像MVC中的View一样查看 - 仅图形表示 . ViewModel是通用View和专用Model之间的Glue . 某种适配器 . 不应该违反DRY .
我认为是的,vanilla MVVM确实违反了DRY . 但我已经开始the PDX library,我认为在很多情况下我可以解决这个问题 . 我写了this post,我相信这个问题 .
基本上,我的目标(其中之一)是让ViewModel不担心UI通知 . PDX项目仍处于起步阶段,但如果您正在阅读这个问题,您可能会发现它很有用,我将非常感谢您提供的任何反馈 .