PersonViewModel继承自Person - 聪明或代码味道?

时间:2010-01-28 20:09:06

标签: c# asp.net-mvc design-patterns

在我的ASP.NET应用中,我有一个Person和一个PersonViewModel

我的PersonLinqToSql生成。它具有将保留在数据库中的属性。

我的PersonViewModel包含Person所有内容,还有一对“选择列表”,用于填充组合框,以及FormattedPhoneNumber(基本上是{ {1}}添加了破折号和括号。)

我最初只是在我的PhoneNumber上创建了Person属性,但我认为这意味着页面必须“知道”某些内容是PersonViewModel属性还是{{ 1}}属性。例如,对于名称,视图会请求Person,但对于电话号码,视图会请求PersonViewModel。如果我使用继承,那么视图所需的所有内容将始终是视图模型的直接属性,因此pvm.Person.Name

这听起来不错,但是,这里没有真正的“是一种”关系(也就是说,我真的认为“pvm.FormattedPhoneNumber a是不合理的pvm.Name),它似乎面对“更喜欢构图而不是继承”。但是,我很难想到我需要能够换掉PersonViewModel的场景。如果我这样做,那就不再是Person

你说什么?继承Person或保留PersonViewModel作为财产(或完全不同的其他内容)?为什么呢?

更新

感谢所有答案。

看起来继承理念几乎被普遍拒绝,并且出于某些合理的原因:

  1. 将类解耦允许Person仅包含 域模型中所需的属性以及任何其他属性。通过继承,您可以自然地从域模型中公开所有公共属性,这可能不是一个好主意。

  2. Person不会因为域模型发生变化而自动更改。

  3. 正如Jay所说,解耦ViewModel有助于特定于视图的验证。

  4. 正如Kieth所提到的,使用Mapper(例如AutoMapper)可以消除很多在映射类之间的公共属性方面的繁琐工作。

6 个答案:

答案 0 :(得分:8)

从对象派生意味着“是一种”关系。因此,在这种情况下,您实际上会说PersonViewModelPerson。看起来这不太可能是你真正想传达的语义,所以在这里使用组合而不是继承。

实际上它可能并没有真正影响应用程序的可维护性(除了在这里和那里有更多的点),但使用组合对我来说当然感觉更清洁。另外,如果它是PersonViewModel,则可能应该知道它正在处理的类型。

答案 1 :(得分:6)

Person设为私有成员,并在PersonViewModel中公开您的视图所需的属性,即使这些属性只是通过Person的相应属性。

pvm.Person.Name是代码味道。


编辑:

您还限制自己在域模型中进行客户端验证和/或验证,后者意味着如果您为Person创建第二个或后续ViewModel,则无法更改Person成员的验证。 (嗯,你可以,但这违反了开放封闭原则,你将视图问题引入域中。)通过从视图中隐藏域对象,你可以给自己一个干净的空间来执行用例 - 在对域对象进行更改之前进行特定验证。域对象可能有自己的一组验证器,但它们可以严格适用于域,而不会出现特定于视图的问题。

答案 2 :(得分:2)

您不应该从Person继承或使用组合在PersonViewModel上拥有Person。相反,您应该将所需的属性添加到PersonViewModel并在它们之间进行映射。像AutoMapper(http://www.codeplex.com/AutoMapper)这样的工具使这很简单。

您不应将您的域模型直接暴露给View,原因有很多,包括安全性(发布不足和过度发布)。

答案 3 :(得分:1)

我认为这里的部分课可能会更好。保留生成的Person类,并在其中添加其他部分Person类。

答案 4 :(得分:1)

一方面:

如果你这样做会怎么样?

Person p = new PersonViewModel { //init some properties };

p会做Person你想做的一切吗?如果是,那么肯定会使用继承。如果它有一些与其真正是PersonViewModel而不是Person的事实相关的特性,那就使用组合。

另一方面:

我倾向于使用继承作为避免大量重复代码的方法。由于您只是从一个父级继承到一个子级(而不是许多子级),因此您首先不会避免大量重复的代码。所以使用继承可能不值得。

答案 5 :(得分:1)

我使用过的一个技巧,不是专门用于ASP.NET MVC,而是使用类似的用例,就是创建一个特别包含ViewModel类特定项的类(即那些不仅仅是隧道传输到person类),并在Person类上提供扩展属性以允许访问扩展属性。这不是严格意义上的经典意义上的ViewModel,但我觉得它允许大部分相同的功能,而不会引入笨拙的继承或使用隧道属性的代码重复。

以下是我正在谈论的一个简单示例:

public static class PersonExtensions {
   public PersonViewData ViewData(this Person p) {
       return new PersonViewData(p);
   }
}

public class PersonViewData {
     public PersonViewData(Person p) {
         this._person = p;
     }

     private Person p;

     public string FormattedPhoneNumber {
         get { return p.PhoneNumber.ToPrettyString(); // or whatever }
     }
}