我制作的ViewModels看起来像其他类一样可疑,并且它们似乎需要大量的代码重复,例如在我目前的项目中:
所以我的 ViewModel与我的Model 基本相同,只是所有OnPropertyChanged功能都与View绑定。
似乎我重构并扩展了我对模型所做的每一点改变,我必须对ViewModel进行 镜像更改。
这似乎违反了模式的基本规则不要重复自己。
我是否错误地实现了MVVM模式,或者只是MVVM的固有特性,模型和ViewModel之间始终存在一对一的重复?
答案 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)
考虑到刚才所说的,我在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项目仍处于起步阶段,但如果您正在阅读此问题,您可能会发现它很有用,我将非常感谢您提供的任何反馈。