我刚刚开始阅读DDD。我无法完全掌握实体与值对象的概念。有人可以解释当值对象被设计为实体对象时系统可能面临的问题(可维护性、性能等)吗?例子会很棒...
10 回答
简化为本质区别,身份对实体很重要,但对值对象无关紧要。例如,某人的姓名是一个值对象。客户实体可能由客户名称(值对象)、List<Order> OrderHistory(实体列表)和可能的默认地址(通常是值对象)组成。客户实体有一个 ID,每个订单都有一个 ID,但名称不应该;一般来说,无论如何,在对象模型中,地址的身份可能并不重要。
值对象通常可以表示为不可变对象;更改值对象的一个属性本质上会破坏旧对象并创建一个新对象,因为您并不像关心内容那样关心身份。正确地,只要对象的属性与另一个实例的属性相同,Name 上的 Equals 实例方法就会返回“true”。
但是,更改像客户这样的实体的某些属性并不会破坏客户;客户实体通常是可变的。身份保持不变(至少在对象被持久化后)。
您可能在没有意识到的情况下创建了值对象;每当您通过创建细粒度类来表示实体的某些方面时,您都会得到一个值对象。例如,对有效值有一些限制但由更简单的数据类型组成的类 IPAddress 将是一个值对象。EmailAddress 可以是字符串,也可以是具有自己的一组行为的值对象。
很有可能即使在您的数据库中具有标识的项目在您的对象模型中也没有标识。但最简单的情况是一些有意义的属性组合在一起。当您可以将它们组合为 Customer.Name 时,您可能不希望拥有 Customer.FirstName、Customer.LastName、Customer.MiddleInitial 和 Customer.Title;当您考虑持久性时,它们可能会成为数据库中的多个字段,但您的对象模型并不关心。
由其所有属性共同定义的任何对象都是值对象。如果任何属性发生更改,您将拥有一个值对象的新实例。这就是为什么值对象被定义为不可变的原因。
如果对象没有完全由其所有属性定义,则存在组成对象身份的属性子集。其余属性可以更改而无需重新定义对象。这种对象不能被定义为不可变的。
一种更简单的区分方法是将值对象视为永远不会更改的静态数据,将实体视为在应用程序中发展的数据。
值类型:
- 值类型本身不存在,依赖于实体类型。
- 值类型对象属于一个实体类型对象。
- 值类型实例的生命周期受拥有实体实例的生命周期的限制。
- 三种值类型:Basic(原始数据类型)、Composite(地址)和 Collection(Map、List、Arrays)
实体:
- 实体类型可以单独存在(Identity)
- 实体有自己的生命周期。它可以独立于任何其他实体而存在。
- 例如:Person、Organization、College、Mobile、Home 等。每个对象都有自己的身份
我不知道以下内容是否正确,但我会说,在地址对象的情况下,我们希望将其用作值对象而不是实体,因为对实体的更改将反映在所有链接对象上(例如一个人)。
举个例子:你和其他人住在你的房子里。如果我们使用 Entity 作为地址,我会争辩说所有 Person 对象都链接到一个唯一的地址。如果有人搬出,您想更新他的地址。如果您要更新地址实体的属性,所有人都会有不同的地址。在值对象的情况下,我们将无法编辑地址(因为它是不可变的),我们将被迫为该人提供一个新地址。
这听起来对吗?我必须说,在阅读了 DDD 书之后,我仍然/现在对这种差异感到困惑。
更进一步,这将如何在数据库中建模?您是否会将 Address 对象的所有属性作为 Person 表中的列,或者您是否会创建一个单独的 Address 表,该表也将具有唯一标识符?在后一种情况下,住在同一所房子里的每个人都有不同的 Address 对象实例,但除了 ID 属性之外,这些对象都是相同的。
地址可以是依赖于业务进程的实体或值对象。地址对象可以是快递服务应用程序中的实体,但地址可以是其他应用程序中的值对象。在快递申请中,地址对象的身份问题
Entities
3和的区别Value Objects
标识符与结构相等:实体具有标识符,如果它们具有相同的标识符,则实体是相同的。手外的值对象具有结构相等性,当所有字段相同时,我们认为两个值对象相等。值对象不能有标识符。
可变性与不变性:值对象是不可变的数据结构,而实体在其生命周期中会发生变化。
寿命:值对象应该属于实体
我在另一个帖子中问过这个问题,我想我仍然很困惑。我可能会将性能考虑与数据建模混淆。在我们的编目应用程序中,客户在需要时不会更改。这听起来很愚蠢 - 但是客户数据的“读取”远远超过“写入”,并且由于许多 Web 请求都针对对象的“活动集”,我不想一次又一次地加载客户。因此,我为 Customer 对象走上了一条不可变的道路——加载它、缓存它,并为 99% 的(多线程)想要查看 Customer 的请求提供相同的服务。然后,当客户更改某些内容时,请让“编辑器”创建新客户并使旧客户无效。
我担心的是,如果许多线程看到相同的客户对象并且它是可变的,那么当一个线程开始更改时,其他线程就会出现混乱。
我现在的问题是,1)这是否合理,以及 2)如何最好地做到这一点而不复制大量关于属性的代码。
我可以用一个非常简单的句子说,我们有三种类型的相等:
- 标识符相等性:一个类具有 id 字段,并且将两个对象与其 id 字段值进行比较。
- 引用相等:如果对两个对象的引用在内存中具有相同的地址。
- 结构相等:如果两个对象的所有成员都匹配,则两个对象相等。
标识符相等仅指实体,结构相等仅指值对象。事实上,值对象没有 id,我们可以互换使用它们。值对象也必须是不可变的,实体可以是可变的,值对象在数据库中不会有任何表。
一个实体:
有身份
包含值对象
可能包含其他实体
可以是可变的
一个值对象:
没有身份
必须是不可变的
考虑以下来自Wikipedia的示例,以便更好地理解值对象和实体之间的区别:
价值对象:人们在兑换美元钞票时,一般不区分每张唯一的钞票;他们只关心美元钞票的面值。在这种情况下,美元钞票是价值对象。但是,美联储可能会关注每张独特的票据;在这种情况下,每个法案都是一个实体。
实体:大多数航空公司在每个航班上唯一区分每个座位。在这种情况下,每个席位都是一个实体。然而,西南航空、易捷航空和瑞安航空并不区分每个座位;所有座位都一样。在这种情况下,座位实际上是一个值对象。