public record Address
{
private Address(
string street,
string city,
string state,
string country,
string zipCode)
{
Street = street;
City = city;
State = state;
Country = country;
ZipCode = zipCode;
}
public string Street { get; init; }
public string City { get; init; }
public string State { get; init; }
public string Country { get; init; }
public string ZipCode { get; init; }
public static Result<Address> Create(
string street,
string city,
string state,
string country,
string zipCode)
{
// بررسی معتبر بودن آدرس
return new Address(street, city, state, country, zipCode);
}
}
🏗 کلاس پایه (Base Class)
روش جایگزین برای پیادهسازی Value Objectها، استفاده از یک کلاس پایه به نام ValueObject است.
این کلاس، وظیفهٔ مقایسهٔ ساختاری (structural equality) را از طریق متد انتزاعی GetAtomicValues بر عهده دارد.
کلاسهای مشتقشده از ValueObject باید این متد را پیادهسازی کنند و اجزایی را که در مقایسهٔ برابری مؤثر هستند، مشخص نمایند.
✨ مزایای استفاده از کلاس پایه
صراحت (Explicitness): بهوضوح مشخص است که کدام کلاسها در دامنه (domain) شما نقش Value Object دارند.
کنترل بر اجزای برابری: میتوانید دقیقاً تعیین کنید چه مقادیری باید در مقایسهٔ برابری در نظر گرفته شوند.
در زیر یک پیادهسازی نمونه از کلاس پایهٔ ValueObject را مشاهده میکنید که در بسیاری از پروژهها کاربرد دارد:
public abstract class ValueObject : IEquatable<ValueObject>
{
public static bool operator ==(ValueObject? a, ValueObject? b)
{
if (a is null && b is null)
{
return true;
}
if (a is null || b is null)
{
return false;
}
return a.Equals(b);
}
public static bool operator !=(ValueObject? a, ValueObject? b) =>
!(a == b);
public virtual bool Equals(ValueObject? other) =>
other is not null && ValuesAreEqual(other);
public override bool Equals(object? obj) =>
obj is ValueObject valueObject && ValuesAreEqual(valueObject);
public override int GetHashCode() =>
GetAtomicValues().Aggregate(
default(int),
(hashcode, value) =>
HashCode.Combine(hashcode, value.GetHashCode()));
protected abstract IEnumerable<object> GetAtomicValues();
private bool ValuesAreEqual(ValueObject valueObject) =>
GetAtomicValues().SequenceEqual(valueObject.GetAtomicValues());
}
🧩 نمونه پیادهسازی Value Object آدرس
در ادامه، Address را به عنوان یک Value Object پیادهسازی کردهایم که از ValueObject ارثبری میکند و متد GetAtomicValues را برای مقایسهٔ اجزای داخلی بازنویسی میکند:
public sealed class Address : ValueObject
{
public string Street { get; init; }
public string City { get; init; }
public string State { get; init; }
public string Country { get; init; }
public string ZipCode { get; init; }
protected override IEnumerable<object> GetAtomicValues()
{
yield return Street;
yield return City;
yield return State;
yield return Country;
yield return ZipCode;
}
}