DDD中的值对象 - 为什么不可变?

Hob*_*bes 33 domain-driven-design value-objects

我不明白为什么DDD中的值对象应该是不可变的,我也不知道如何轻松完成.(如果重要的话,我专注于C#和实体框架.)

例如,让我们考虑经典的Address值对象.如果你需要改变"解放路123号"到"123主 ",我为什么要需要构建一个全新的对象,而不是说myCustomer.Address.AddressLine1 ="123大街"的?(即使实体框架支持结构,这仍然是一个问题,不是吗?)

我理解(我认为)价值对象没有身份并且是域对象的一部分的想法,但有人可以解释为什么不变性是一件好事吗?


编辑:我在这里的最后一个问题应该是"有人可以解释为什么不变性是适用于价值对象的好东西吗?" 对困惑感到抱歉!


编辑:为了clairfy,我不是在询问CLR值类型(与引用类型相比).我问的是价值对象的更高级DDD概念.

例如,这里是实现不可变的值类型实体框架劈上下的方式: http://rogeralsing.com/2009/05/21/entity-framework-4-immutable-value-objects.基本上,他只是让所有的安装者都私密.为什么要经历这样做的麻烦?

Rog*_*son 43

忽略关于线程安全等的所有疯狂的答案,这与DDD无关.(我还没有看到线程安全的O/R映射器或其他DDD友好dal)

想象一下权重的价值对象.假设我们有一个KG值对象.

样本(为清晰起见编辑):

var kg75 = new Weight(75);
joe.Weight = kg75;
jimmy.Weight = kg75;
Run Code Online (Sandbox Code Playgroud)

现在如果我们这样做会发生什么:

jimmy.Weight.Value = 82;
Run Code Online (Sandbox Code Playgroud)

如果我们仍然使用相同的对象引用,那也会改变joe的权重.请注意,我们为joe和jimmy分配了一个代表75千克的对象.当jimmy增加重量时,不是kg75对象已经改变,它是jimmys重量已经改变,因此,我们应该创建一个代表82 kg的新对象.

但是如果我们有一个新的会话并在一个干净的UoW中加载joe和jimmy呢?

 var joe = context.People.Where(p => p.Name = "joe").First();
 var jimmy = context.People.Where(p => p.Name = "jimmy").First();
 jimmy.Weight.Value = 82;
Run Code Online (Sandbox Code Playgroud)

那会发生什么?好吧,因为在你的情况下EF4会加载joe和jimmy以及它们的重量而没有任何标识,我们会得到两个不同的重量对象,当我们改变jimmys重量时,joe仍然会像以前一样重量.

因此,对于相同的代码,我们会有两种不同的行为.如果对象引用仍然相同,则joe和jimmy都会获得新的权重.如果joe和jimmy被加载到一个干净的哇,只有其中一个会受到更改的影响.

那将是非常无情的imo.

通过使用不可变的VO,您可以在两种情况下获得相同的行为,并且在构造对象图时,您仍然可以在较小的内存占用空间中重用对象引用.

  • 只有抛弃理论方面的内容并不能说明如果忽视这些问题会导致麻烦的原因.IMO,如果您能够在更具体的情况下看到为什么某些事情会失败,那么更容易理解这些概念存在的原因. (6认同)
  • 这个论点是有缺陷的.从技术角度来看,您是出于概念原因而选择的.值对象和实体的这些概念存在于任何特定实现之外. (4认同)
  • 它涵盖了我们为什么需要不变的值对象,如果你使用特定的技术或任何不论,重量75千克是不可变的,不管你使用EF4或任何其他技术.如果某人的体重发生了变化,那么代表75公斤的物体不应该改变,你应该为那个人分配一个新的体重. (3认同)
  • +1。用一个很好的例子回答了这个问题。类似于 Martin Fowler 的解释(http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable)。 (2认同)

jas*_*son 33

为什么6不可变?

理解这一点,你就会理解为什么Value Objects应该是不可变的.

编辑:我现在将对话框提到这个答案.

6是不可改变的,因为它6的身份是由它所代表的东西决定的,即有六种东西的状态.你不能改变它6代表那个.现在,这是Value Objects的基本概念.他们的价值取决于他们的国家.但是,实体不是由其州决定的.A Customer可以更改姓氏或地址,但仍然相同Customer.这就是Value Objects应该是不可变的原因.他们的国家决定他们的身份 如果他们的州改变,他们的身份应该改变.

  • @Hobbes:好的,但为什么`6`总是`6`?这是因为`6`的身份是由它所代表的东西决定的,即拥有六个东西的状态.你不能改变`6`代表那个.现在,这是Value Objects的基本概念.他们的价值取决于他们的国家.但是,实体不是由其州决定的."客户"可以更改姓氏或地址,但仍然是"客户".这就是Value Objects应该是不可变的原因.他们的国家决定他们的身份 如果他们的州改变,他们的身份应该改变. (7认同)
  • 我见过这样的例子,看起来太简单了.6是不可变的,因为像String一样,你在它上面执行的任何操作都会为你提供一个新对象.6 + 2不是已改为8的数字6; 这是一个新的数字.我明白了,但我不喜欢这个比喻.在Address的情况下,类型更复杂,并且在其上定义返回新类型的操作更加困难(并且感觉很愚蠢).例如,newAddress = oldAddress.ChangeLine1("123 Main Street")? (2认同)
  • 最后的评论非常完美. (2认同)

Dav*_*vid 10

我参加派对的时间已经很晚了,但我自己一直在想这个问题.(感谢任何评论.)

我认为这里没有明确引用,但我认为埃文斯对不变性的提及主要是在分享的背景下:

为了安全地共享对象,它必须是不可变的:除非完全替换,否则不能更改它.(埃文斯p100)

在Evan的书中还有一个侧栏,名为"Is Is a value a Value Object?谁在问?".

如果每个室友都要求订购电气服务[即如果两个客户拥有相同的地址],公司就需要实现它.[所以]地址是一个实体.(埃文斯p98)

在您给出的示例中,假设客户的家庭和公司地址均为123 Main Street.当您进行描述校正时,两个地址都会发生变化吗?如果是这样,如果我正确地阅读埃文斯,听起来你真的有一个实体.

举一个不同的例子,假设我们有一个对象来表示客户的全名:

public class FullName
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class Customer
{
    public FullName Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

如果没有值对象,以下内容将失败:

[Test]
public void SomeTest() {
    var fullname = new FullName { FirstName = "Alice", LastName = "Jones" };
    var customer1 = new Customer { Name = fullname };
    var customer2 = new Customer { Name = fullname };

    // Customer 1 gets married.
    customer1.Name.LastName = "Smith";

    // Presumably Customer 2 shouldn't get their name changed.
    // However the following will fail.
    Assert.AreEqual("Jones", customer2.Name.LastName);
}
Run Code Online (Sandbox Code Playgroud)

在优势方面,有些是在In DDD中获得的,有价值对象的实际优势是什么?.值得注意的是,您只需要在创建时验证VO.如果你这样做,那么你知道它总是有效的.


Ara*_*ram 5

这可能不是完整的答案.我只回答你关于不变性的优点的问题.

  1. 因为Immutable对象是线程安全的.由于它们不能改变状态,因此它们不会被线程干扰破坏或在不一致状态下被观察到.
  2. 可以轻松地共享或缓存对不可变对象的引用,而无需复制或克隆它们,因为它们的状态在构造之后无法更改.
  3. 对于Immutability的更多优点,你看这里(LBushkin的答案) String是不可变的优势是什么?

这是 Martin Fowler关于为什么值对象应该是不可变的一个例子.

好吧,虽然没有强制要求VO成为不可变的(即使DDD书并没有说它们必须是不可变的),DDD中使其成为VO的主要想法似乎不是要处理生命周期的复杂性,如实体的那个.在这里查看更多详情.