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

Domain driven design DDD中的值对象-为什么不可变?,domain-driven-design,value-objects,Domain Driven Design,Value Objects,我不明白为什么DDD中的值对象应该是不可变的,也不明白这是如何容易做到的。(我的重点是C#和实体框架,如果这很重要的话。) 例如,让我们考虑经典地址值对象。如果您需要将“123 Main St”更改为“123 MainStreet”,为什么我需要构建一个全新的对象,而不是说myCustomer.Address.AddressLine1=“123 Main Street”?(即使实体框架支持结构,这仍然是一个问题,不是吗?) 我理解(我认为)值对象没有标识并且是域对象的一部分,但是有人能解释为什么

我不明白为什么DDD中的值对象应该是不可变的,也不明白这是如何容易做到的。(我的重点是C#和实体框架,如果这很重要的话。)

例如,让我们考虑经典地址值对象。如果您需要将“123 Main St”更改为“123 MainStreet”,为什么我需要构建一个全新的对象,而不是说myCustomer.Address.AddressLine1=“123 Main Street”?(即使实体框架支持结构,这仍然是一个问题,不是吗?)

我理解(我认为)值对象没有标识并且是域对象的一部分,但是有人能解释为什么不变性是一件好事吗


编辑:我在这里的最后一个问题应该是“有人能解释为什么不变性是应用于值对象的一件好事吗”?“很抱歉造成混淆


编辑:对Clarify来说,我不是在问CLR值类型(相对于引用类型)。我问的是价值对象的更高层次的DDD概念


例如,以下是一种为实体框架实现不可变值类型的黑客方法:。基本上,他只是让所有二传球员都成为私人球员。为什么要费心这么做呢?

这可能不是完整的答案。我只是回答你关于不变性的好处的问题

  • 因为不可变对象是线程 安全的。因为他们不能改变 国家,他们不能被腐败 观察到螺纹干涉或磨损 不一致的状态
  • 对不可变对象的引用 对象可以很容易地共享或删除 缓存而无需复制或 克隆它们,因为它们的状态无法更改 在建造之后发生了变化
  • 更多关于不变性的优点 你看这里(伊布什金的回答)
  • Martin Fowler关于为什么值对象应该是不可变的

    好的,虽然使VO不可变不是强制性的(甚至DDD书籍也没有说它们必须是不可变的),但在DDD中,使其成为VO的主要思想似乎不是处理实体的生命周期复杂性。看这里。

    为什么6是不可变的

    理解这一点,您就会理解为什么值对象应该是不可变的

    编辑:现在我将把我们的对话提升到这个答案中


    6
    是不可变的,因为
    6
    的身份是由它所代表的东西决定的,即某事物的六个状态。您无法更改
    6
    表示的内容。这是价值对象的基本概念。它们的价值由它们的状态决定。然而,一个实体不是由其国家决定的。
    客户
    可以更改他们的姓氏或地址,但仍然是相同的
    客户
    。这就是为什么值对象应该是不可变的。他们的国家决定了他们的身份;如果他们的状态改变,他们的身份也应该改变。

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

    想象一个权重值对象。 假设我们有一个KG值的对象

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

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

    jimmy.Weight.Value = 82;
    
    如果我们仍然使用相同的对象引用,这也会改变joe的权重。 请注意,我们为joe和jimmy分配了一个代表75kg的对象。 当jimmy增加体重时,改变的不是kg75对象,而是jimmy的体重,因此,我们应该创建一个代表82公斤的新对象

    但是,如果我们有一个新的会议,并加载乔和吉米在一个干净的UoW

     var joe = context.People.Where(p => p.Name = "joe").First();
     var jimmy = context.People.Where(p => p.Name = "jimmy").First();
     jimmy.Weight.Value = 82;
    
    那么会发生什么?好吧,因为在你的例子中EF4会加载joe和jimmy以及他们的重量,而没有任何标识,所以我们会得到两个不同的重量对象,当我们改变jimmy的重量时,joe的重量仍然和以前一样

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

    这在国际海事组织是不一致的


    通过使用不可变VO,在这两种情况下,您将获得相同的行为,并且在构建对象图时,您仍然可以重用对象引用以获得更小的内存占用。

    我参加聚会已经很晚了,但我自己一直在想这个问题。(如有任何意见,我们将不胜感激。)

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

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

    埃文的书中还有一个边栏,叫做“地址是一个价值对象吗?谁在问?”

    如果室友们都打电话订购电气服务(如果两个顾客的地址相同),公司会这样做 我们需要认识到这一点。[所以]地址是一个实体。(埃文斯第98页)

    在您给出的示例中,假设客户的家庭和公司地址都是Main Street 123。当你做出你描述的更正时,两个地址都会改变吗?如果是这样,如果我读对了埃文斯,听起来你真的有一个实体

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

    public class FullName
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
    
    public class Customer
    {
        public FullName Name { get; set; }
    }
    
    如果没有值对象,以下操作将失败:

    [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);
    }
    

    就优势而言,一般而言,有些优势是在。值得注意的是,您只需在创建时验证VO。如果您这样做,那么您知道它总是有效的。

    值对象需要是不可变的

    在许多情况下,不变的对象确实使生活更简单。。。它们可以使并发编程方式更加安全和干净

    让我们考虑值对象是可变的。< /P>

    class Name{
    string firstName,middleName,lastName
    ....
    setters/getters
    }
    
    假设你的原名是理查德·托马斯·库克

    现在让我们假设你只改变了
    class Name{
    string firstName,middleName,lastName
    ....
    setters/getters
    }
    
    public class Money
    {
        protected readonly decimal Value;
    
        public Money(decimal value)
        {
            Value = value;
        }
    
        public Money Add(Money money)
        {
            return new Money(Value + money.Value);
        }
    
        // ...
    
    
        // Equality (operators, Equals etc) overrides (here or in a Value Object Base class). 
        // For example:
    
        public override bool Equals(object obj)
        {
            return Equals(obj as Money);
        }
    
        public bool Equals(Money money)
        {
            if (money == null) return false;
            return money.Value == Value;
        }
    }