在这样一个简单的例子中,答案是肯定的(它是不合理的冗余)。但是,假设页面将包含不止一个Model对象。您可能具有页面状态以及必须跟踪所有其他Model对象。这是在ViewModel中完成的。
例如,您可以在状态栏中显示有关已登录用户的附加信息,以及运行以检测文本文件更改的服务。
您还可以拥有用于编辑Student对象的表单。如果您打算验证这些更改,那么在修改得到验证之前,您不希望直接编辑Student对象。在这种情况下,ViewModel可以充当临时存储位置。
关于上述内容的说明:在模型中进行验证并不罕见,但即使如此,在编辑表单的过程中,您可能仍希望用户能够输入无效值。例如,如果您的模型不允许在字段中出现零长度值,则仍然希望用户能够删除该值,转到另一个字段(例如复制它),然后返回该字段并完成编辑(粘贴)。如果您直接与模型绑定,则您的验证逻辑可能无法像您希望的那样处理这种“中间”、“尚未完成”的状态。例如,您可能不想在用户完成并点击“保存”之前就向其显示验证错误。
您还可能在ViewModel中拥有命令对象来处理按钮点击等操作。这些将是特定于领域的对象,在模型中将无用。
当您需要过滤或以某种方式暂时“修改”模型对象以在屏幕上获得有用信息时,ViewModel也很有用。例如,您可能希望显示系统中所有用户的列表以及实时列表中排名前十的表现者(每10秒更新一次)。或者您可能希望显示报告列表以及显示整体使用率的图表等。过滤、排序和自定义数据将在ViewModel中进行。
另一方面,模型通常尽可能纯净。理想情况下,您只希望有
POCOs,这些POCOs通常正好对应您的持久存储(数据库等)。如果您的持久存储具有FirstName和LastName字段,则模型也将具有这些字段。仅在ViewModel中,您才会将它们组合在一起以获取Name字段(根据视图的需要,可以是“First Last”或“Last, First”)。例如:
namespace Model
{
public class Student
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Class
{
public string Name { get; set; }
public float Score { get; set; }
}
}
namespace ViewModel
{
public class EditStudentRecordViewModel
{
private Model.Student _student;
private IEnumerable<Model.Class> _studentClasses;
public string FullName
{
return _student.LastName + ", " + _student.FirstName;
}
public string FirstName { get; set; }
public string LastName { get; set; }
public IEnumerable<Model.Class> PassingClasses
{
get
{
return _studentClasses.Where( c => c.Score >= 78 );
}
}
public IEnumerable<Model.Class> FailingClasses
{
get
{
return _studentClasses.Where( c => c.Score < 78 );
}
}
public void Save()
{
List<string> l_validationErrors = new List<string>();
if ( string.IsNullOrEmpty( this.FirstName ) )
l_validationErrors.Add( "First Name must not be empty." );
if ( string.IsNullOrEmpty( this.LastName ) )
l_validationErrors.Add( "Last Name must not be empty." );
if ( l_validationErrors.Any() )
return;
_student.FirstName = this.FirstName;
_student.LastName = this.LastName;
Model.Utilities.SaveStudent( _student );
}
}
}