类复制模式?


11

我目前是我当前项目的独立开发人员。我从另一个离开公司的开发商那里继承了这个项目。它是C#中的模型视图控制器样式的Web应用程序。它使用实体框架进行对象关系映射。域模型中的类型有两组不同的类。一组用于与ORM进行交互,另一组用作MVC系统中的模型。例如,可能有两个类别,如下所示:

public class Order{
    int ID{get;set;}
    String Customer{get;set;}
    DateTime DeliveryDate{get;set;}
    String Description{get;set;}
}

public class OrderModel{
    String Customer{get;set;}
    DateTime DeliveryDate{get;set;}
    String Description{get;set;}
    public OrderModel( Order from){
        this.Customer= from.Customer;
        // copy all the properties over individually
    }
    public Order ToOrder(){
        Order result =new Order();
        result.Customer = this.Customer;
        // copy all the properties over individually
    }

}

我可以想到这种方法的一些弊端(如果发生某些更改,有更多的地方可以更改代码,有更多的对象坐在内存中,有更多的时间花在复制数据上),但是我不确定这有什么好处。我认为模型类具有更大的灵活性吗?但是我也可以通过对实体类进行子类化来实现。我的倾向是合并这两个类组,或者使模型类成为实体类的子类。那么我在这里错过了重要的事情吗?这是我不知道的常见设计模式吗?是否有充分的理由不进行我正在考虑的重构?

更新

这里的一些答案使我意识到我对该项目的最初描述缺少一些重要的细节。项目中还有第三组类:页面模型类。它们是实际用作支持页面的模型的模型。它们还包含特定于UI的信息,不会与订单一起存储在数据库中。页面模型类的示例可能是:

public class EditOrderPagelModel
{
    public OrderModel Order{get;set;}
    public DateTime EarliestDeliveryDate{get;set;}
    public DateTime LatestAllowedDeliveryDate{get;set;}
}

我完全看到这第三组的效用在这里是截然不同的,并且没有计划将其与其他东西合并(尽管我可能会重命名)。

应用程序的API当前也使用了模型组中的类,我也希望对这种想法是否有用感兴趣。

我还应该提到,在这里用字符串表示客户是为了简化示例,而不是因为在系统中实际上是用这种方式表示的。实际系统将客户作为域模型中的独特类型,具有自己的属性


1
“我从另一个离开公司的开发人员那里继承了该项目”,这里有一个专门介绍这种故事的站点。

关于您的更新:似乎太多的间接访问没有带来足够的收益。
罗伯特·哈维

@RobertHarvey您指的是太多的间接寻址?
罗伯特·里基茨

1
OrderModel。OrderViewModel(您可能将其称为EditOrderPageModel)已经足够间接。请参阅下面的答案。
罗伯特·哈维

@RobertHarvey这听起来像是我实际上想问的问题的答案
Robert Ricketts

Answers:


16

那么我在这里错过了重要的事情吗?

尽管它们看起来像相同的事物,并且在域中表示相同的事物,但是它们不是相同的(OOP)对象。

一种是Order众所周知的代码的数据存储部分。另一个是OrderUI所熟知的。这些类具有相同的属性是一个令人意外的巧合,但不能保证它们具有相同的属性。

或从另一个角度来看它,请考虑“单一责任原则”。这里的主要动机是“一个阶级有一个改变的理由”。特别是在处理诸如ORM和/或UI框架这样的普遍框架时,您的对象需要很好地隔离,因为对框架的更改通常会导致您的实体发生更改。

通过将实体绑定到两个框架,您将变得更加糟糕。


7

View Model的目的是通过两种方式提供去耦:通过提供View所需形状的数据(与模型无关),以及(特别是在MVVM中)通过将部分或全部View逻辑从View中推回来视图模型。

在完全规范化的模型中,Customer不会在模型中由字符串表示,而是由ID表示。如果需要客户的名称,模型将使用“订单”表中的“客户ID”从“客户”表中查找名称。

另一方面,视图模型可能Name对客户而不是ID 更感兴趣。除非在模型中更新客户,否则不需要ID。

另外,视图模型可以并且通常确实采用不同的形状(除非它是纯CRUD)。发票视图模型对象可能具有客户名称,地址和订单项,但是该模型包含客户和描述。

换句话说,通常不会以显示发票的方式存储发票。


3

在某些方面,您是对的:它们都引用相同的域对象,称为order。但是,到目前为止,您错了,它不是真正的重复,而只是不同的表示形式-来自不同的目的。如果您想保留订单,例如,您想访问每一列。但是您不想将其泄漏到外部。除了通过电线推动整个实体,这并不是您真正想要的。我知道有一些案例,其中将MB的集合orders发送到客户端,其中几kb就足够了。

长话短说-这是您要这样做的主要原因:

1)封装-您的应用程序的信息隐藏

2) Small Models快速序列化且易于传输

3)尽管它们是重叠的,但它们有不同的用途,因此应该有不同的对象。

4)有时对于不同的查询具有不同的Value-Objects有意义。我不知道C#中的情况如何,但是在Java中可以使用POJO来收集结果。如果我需要一个,则order使用Order -Entity,但是如果我只需要一些填充有数据量的列,则使用较小的 Objects效率更高。


实体框架的结构方式是,实体是普通的旧C#对象,因此通过电线发送它们并不是什么大问题。我确实看到了在某些情况下能够仅发送订单内容的一部分的实用程序。
罗伯特·里基茨

正如我所说的,一个或一束不是问题。但是在某些情况下,您拥有MB的数据,这会减慢加载时间。想想移动加载时间
Thomas Junk

0

(免责声明:我只看到它是以这种方式使用的。我可能误解了这样做的真正目的。请以怀疑的态度对待这个答案。)

这里是另一个失踪位:之间的转换OrderOrderModel

Order班是依赖于你的ORM,而OrderModel绑定到您的视图模型的设计。

通常,这两种方法将“以一种神奇的方式”提供(有时称为DI,IoC,MVVM或另一种方式)。也就是说OrderModel,与其将这两种转换方法实现为的一部分,它们将改为属于致力于这些事物之间转换的类。该类将以某种方式在框架中注册,以便框架可以轻松查找它。

public class OrderModel {
    ...
    public OrderModel(Order from) { ... }
    public Order ToOrder() { ... }
}

这样做的原因是,无论何时从OrderModelOrder或从到的交叉,您都知道某些数据必须已从ORM加载或保存到ORM。


如果我俩都留在身边,我肯定会设置它们以使转换更加自动化
Robert Ricketts

@RobertRicketts我看到的东西是这样的:IMvxValueConverter。尽管我可能误解了它的目的。
rwong 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.