领域驱动设计中,当实体包含值对象时(如订单包含产品详情),如何设计其等值性判断逻辑?请给出具体案例。
在领域驱动设计(DDD)中,实体(Entity)与其包含的值对象(Value Object)的等值性判断逻辑是设计上的一个关键点。实体的等值性通常基于其标识符(ID),而值对象的等值性基于其值。当实体中包含值对象时,需要确保在判断实体等值性时,值对象的值也被考虑进去。以下是一个具体的案例,假设我们有一个订单(Order)实体,该实体中包含产品详情(ProductDetail)的值对象。
实体 - Order
public class Order
{
public Guid Id { get; private set; }
public DateTime OrderDate { get; private set; }
public List<ProductDetail> ProductDetails { get; private set; }
public Order(DateTime orderDate)
{
Id = Guid.NewGuid();
OrderDate = orderDate;
ProductDetails = new List<ProductDetail>();
}
public void AddProductDetail(ProductDetail productDetail)
{
ProductDetails.Add(productDetail);
}
public override bool Equals(object obj)
{
if (obj is null || GetType() != obj.GetType())
return false;
var other = (Order)obj;
return Id == other.Id &&
OrderDate == other.OrderDate &&
ProductDetails.SequenceEqual(other.ProductDetails);
}
public override int GetHashCode()
{
return HashCode.Combine(Id, OrderDate, ProductDetails);
}
}
值对象 - ProductDetail
public class ProductDetail
{
public string ProductId { get; private set; }
public string Name { get; private set; }
public decimal Price { get; private set; }
public ProductDetail(string productId, string name, decimal price)
{
ProductId = productId;
Name = name;
Price = price;
}
public override bool Equals(object obj)
{
if (obj is null || GetType() != obj.GetType())
return false;
var other = (ProductDetail)obj;
return ProductId == other.ProductId &&
Name == other.Name &&
Price == other.Price;
}
public override int GetHashCode()
{
return HashCode.Combine(ProductId, Name, Price);
}
}
解释
- Order 类的 Equals 方法:在
Order类中,Equals方法首先检查对象类型,然后比较标识符Id和OrderDate。接着,使用SequenceEqual方法比较两个ProductDetails列表,确保它们的内容相同。 - ProductDetail 类的 Equals 方法:在
ProductDetail类中,Equals方法也检查对象类型,然后比较ProductId、Name和Price。这是值对象等值性的核心。 - GetHashCode 方法:为了确保哈希代码的一致性,
GetHashCode方法在Order和ProductDetail类中都实现了,使用HashCode.Combine方法组合各个属性的哈希代码。
通过这种方式,我们确保了在比较两个 Order 实体时,不仅考虑了它们的标识符和订单日期,还考虑了包含在订单中的所有产品详情的值。这确保了等值性判断的准确性和完整性。