我不明白为什么DDD中的值对象应该是不可变的,我也不知道如何轻松完成。 (如果重要的话,我专注于C#和实体框架。)
例如,让我们考虑一下经典的Address值对象。如果您需要将“123 Main St”更改为“123 Main Street ”,为什么我需要构建一个全新的对象而不是说myCustomer.Address.AddressLine1 = “123大街”? (即使实体框架支持结构,这仍然是一个问题,不是吗?)
我理解(我认为)价值对象没有身份并且是域对象的一部分的想法,但有人可以解释为什么不变性是一件好事吗?
编辑:我的最后一个问题应该是“有人可以解释为什么不变性是一件好事 应用于价值对象 ?”抱歉混乱!
编辑:为了克服,我不是在询问CLR值类型(与引用类型相比)。我问的是价值对象的更高级DDD概念。
例如,这是一种为实体框架实现不可变值类型的黑客方法:http://rogeralsing.com/2009/05/21/entity-framework-4-immutable-value-objects。基本上,他只是让所有的安装者都私密。为什么要经历这样做的麻烦?
答案 0 :(得分:42)
忽略与线程安全等有关的所有疯狂答案,这与DDD无关。 (我还没有看到线程安全的O / R映射器或其他DDD友好dal)
想象一下权重的值对象。 假设我们有一个KG值对象。
样本(为清晰起见而编辑):
var kg75 = new Weight(75);
joe.Weight = kg75;
jimmy.Weight = kg75;
现在如果我们这样做会发生什么:
jimmy.Weight.Value = 82;
如果我们仍然使用相同的对象引用,那也会改变joe的权重。 请注意,我们为joe和jimmy分配了一个代表75千克的对象。 当jimmy增加体重时,不是kg75对象已经改变,它是jimmys体重已经改变,因此,我们应该创建一个代表82公斤的新物体。
但是如果我们有一个新的会话并在一个干净的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;
那会怎么样?好吧,因为在你的情况下EF4会加载joe和jimmy以及它们的重量而没有任何标识,我们会得到两个不同的重量对象,当我们改变jimmys的重量时,joe仍然会像以前一样重量。
因此,对于相同的代码,我们会有两种不同的行为。 如果对象引用仍然相同,则joe和jimmy都会获得新的权重。 如果joe和jimmy加载在一个干净的哇,只有其中一个会受到更改的影响。
那将是非常不稳定的imo。
通过使用不可变的VO,在两种情况下都会得到相同的行为,并且在构造对象图时,您仍然可以重用对象引用以减少内存占用。
答案 1 :(得分:33)
为什么6不可变?
理解这一点,你就会理解为什么Value Objects应该是不可变的。
编辑:我现在将对话框提到这个答案。
6
是不可变的,因为6
的身份是由它代表的东西决定的,即有六个东西的状态。您无法更改6
表示的内容。现在,这是Value Objects的基本概念。他们的价值取决于他们的国家。但是,实体不是由其州决定的。 Customer
可以更改其姓氏或地址,但仍然是Customer
。这就是Value Objects应该是不可变的原因。他们的国家决定他们的身份如果他们的状态发生变化,他们的身份应该改变。
答案 2 :(得分:10)
我参加晚会很晚,但我自己一直在想这个。 (感谢任何评论。)
我认为这里没有明确引用,但我认为埃文斯对不变性的提及主要是在分享的背景下:
为了安全地共享对象,它必须是不可变的:它 除非完全更换,否则无法更改。 (Evans p100)
在埃文的书中还有一个侧栏,名为“地址是一个价值对象吗?谁在问?”。
如果室友都打电话订购电气服务[即如果两个客户有相同的地址],公司会 需要实现它。 [所以]地址是一个实体。 (埃文斯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; }
}
如果没有值对象,以下内容将失败:
[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);
}
就一般优势而言,有些是在In DDD, what are the actual advantages of value objects?获得的。值得注意的是,您只需要在创建时验证VO。如果你这样做,那么你知道它总是有效的。
答案 3 :(得分:5)
这可能不是完整的答案。我只回答你关于不变性的优点的问题。
This is an example关于为什么值对象应该是不可变的。
好吧,虽然没有强制要求VO成为不可变的(即使DDD书并没有说它们必须是不可变的),DDD中使其成为VO的主要想法似乎不是要处理生活循环复杂性,如实体的复杂性。看这里for more details。
答案 4 :(得分:0)
值对象需要是不可变的。
在许多情况下,不可变对象确实使生活变得更简单。 ......他们可以使并发编程更加安全和清洁for more info
让我们认为价值对象是可变的。
class Name{
string firstName,middleName,lastName
....
setters/getters
}
让我们说你原来的名字是 Richard Thomas Cook
现在让我们假设您只更改firstName(对Martin)和lastName(对Bond),
如果它不是不可变对象,您将使用这些方法逐个改变状态。有名字的机会
在 Martin Thomas Bond 的最终名称之前,
可变值对象显式必须对1个事务中给出的更改强制执行完整性约束,该事务在Immutable对象中是免费的。 因此,使值对象不可变是有意义的。
答案 5 :(得分:0)
这是很久以前问过的,但是我决定提供一个示例,我觉得它很简单也很容易记住。此外,SO可以为许多开发人员提供参考,我认为任何遇到此问题的人都可以从中受益。
由于它们是由属性定义的,因此值对象被视为不可变的。
价值对象的一个很好的例子是金钱。没关系,您也无法在口袋中分辨出相同的五张一美元钞票。您不在乎该货币的身份,仅在乎其价值及其代表什么。如果有人用一张5美元的钞票换成您钱包里的5美元钞票,这不会改变您仍然还有5美元的事实。
例如,在C#中,您将金钱定义为不可变的值对象:
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;
}
}