DDD的實體、值物件、聚合根的基類和介面:設計與實現

南榮相如談程式設計發表於2021-01-28

1 前置閱讀

在閱讀本文章之前,你可以先閱讀:

  • 什麼是DDD

2 實現值物件

值物件有兩個主要特徵:它們沒有任何標識。它們是不可變的。

我們舉個例子:小明是“浙江寧波”人,小紅也是“浙江寧波”人,小王是“浙江杭州”人,在這個例子中,我們把地址可以獨立出一個值物件出來,我們會遇到了多個物件是否相同的問題,例如小明和小紅的地址應該是相等,小明和小王應該是不相等,這很好理解,我們來看一下例子;

public class Address
{
    public string Province;
    public string City;
}

var xm = new Address { Province = "浙江", City = "寧波" };
var xh = new Address { Province = "浙江", City = "寧波" };
var xw = new Address { Province = "浙江", City = "杭州" };

Console.WriteLine(xm.Equals(xh));
Console.WriteLine(xm.Equals(xw));

讓我們來看看輸出結果:

False
False

這個顯然不符合我們預期,我們需要重寫一下Equals,確保地址值相等的情況下物件相等。

public class Address
{
    public string Province;
    public string City;

    public bool Equals(Address obj)
    {
        return this.Province.Equals(obj.Province) && this.City.Equals(obj.City);
    }
}

var xm = new Address { Province = "浙江", City = "寧波" };
var xh = new Address { Province = "浙江", City = "寧波" };
var xw = new Address { Province = "浙江", City = "杭州" };

Console.WriteLine(xm.Equals(xh));
Console.WriteLine(xm.Equals(xw));

讓我們來看看輸出結果:

True
False

這個顯然符合我們預期了,接下來我們把值物件的Equals方法封裝到基類中。

public abstract class ValueObject
{
    protected abstract IEnumerable<object> GetEqualityComponents();
    public override bool Equals(object obj)
    {
        if (obj == null || obj.GetType() != GetType())
        {
            return false;
        }

        var other = (ValueObject)obj;

        return this.GetEqualityComponents().SequenceEqual(other.GetEqualityComponents());
    }
}

public class Address : ValueObject
{
    public string Province;
    public string City;

    protected override IEnumerable<object> GetEqualityComponents()
    {
        yield return Province;
        yield return City;
    }
}

3 實現實體

實體主要特徵:具有唯一標識。

前面我們講到值物件將特定值都相等的物件視為相等物件,在實體中比較容易理解,標識相等的物件視為相等物件。

public abstract class Entity
{

    #region IEntity Members
    public abstract Guid Id
    {
        get; set;
    }
    #endregion

    public override bool Equals(object obj)
    {
        if (obj == null || !(obj is Entity))
            return false;

        if (Object.ReferenceEquals(this, obj))
            return true;

        if (this.GetType() != obj.GetType())
            return false;

        Entity item = (Entity)obj;

        return item.Id == this.Id;
    }

    public override int GetHashCode()
    {
        return base.GetHashCode();
    }
}

4 實現聚合根

聚合根與實體的區別,實體只在聚合內部進行操作,聚合根是對外打交道的唯一實體。我們在這裡設計時聚合根需要有增改刪狀態欄位。

public enum AggregateState
{
    Added = 1,
    Updated = 2,
    Deleted = 3
}

public abstract class AggregateRoot : Entity
{
    #region IAggregateRoot Members
    public AggregateState AggregateState { set; get; }
    #endregion
}

相關文章