Як правило, об'єкти домену мають властивості, які можуть бути представлені вбудованим типом, але дійсні значення яких є набором значень, які можуть бути представлені цим типом.
У цих випадках значення можна зберігати за допомогою вбудованого типу, але необхідно забезпечити, щоб значення завжди були перевірені в точці введення, інакше ми можемо закінчити роботу з недійсним значенням.
Один із способів вирішити це - зберігати значення як звичай, structякий має єдине private readonlyполе резервного вбудованого типу і конструктор якого підтверджує надане значення. Тоді ми завжди можемо бути впевнені в тому, що лише використовуючи перевірені значення, використовуючи цей structтип.
Ми також можемо надати операторів литих від і до базового вбудованого типу, щоб значення могли безперешкодно входити та виходити як базовий тип.
Візьмемо для прикладу ситуацію, коли нам потрібно представити ім'я доменного об’єкта, а допустимими значеннями є будь-яка рядок, довжиною від 1 до 255 символів включно. Ми могли б представити це за допомогою наступної структури:
public struct ValidatedName : IEquatable<ValidatedName>
{
private readonly string _value;
private ValidatedName(string name)
{
_value = name;
}
public static bool IsValid(string name)
{
return !String.IsNullOrEmpty(name) && name.Length <= 255;
}
public bool Equals(ValidatedName other)
{
return _value == other._value;
}
public override bool Equals(object obj)
{
if (obj is ValidatedName)
{
return Equals((ValidatedName)obj);
}
return false;
}
public static implicit operator string(ValidatedName x)
{
return x.ToString();
}
public static explicit operator ValidatedName(string x)
{
if (IsValid(x))
{
return new ValidatedName(x);
}
throw new InvalidCastException();
}
public static bool operator ==(ValidatedName x, ValidatedName y)
{
return x.Equals(y);
}
public static bool operator !=(ValidatedName x, ValidatedName y)
{
return !x.Equals(y);
}
public override int GetHashCode()
{
return _value.GetHashCode();
}
public override string ToString()
{
return _value;
}
}
У прикладі показано тоталізатор string, implicitоскільки це ніколи не може бути невдалим, але винесений string, explicitоскільки це призведе до недійсних значень, але, звичайно, вони можуть бути implicitабо explicit.
Зауважте також, що цю структуру можна ініціалізувати лише за допомогою кадру string, але можна перевірити, чи не вдасться такий заздалегідь вийти з ладу за допомогою IsValid staticметоду.
Це може бути гарною схемою для примусової перевірки значень домену, які можуть бути представлені простими типами, але я не бачу, що це використовується часто чи пропонується, і мене цікавить, чому.
Отже, моє запитання: що ви бачите як переваги та недоліки використання цього шаблону, і чому?
Якщо ви відчуваєте, що це погана модель, я хотів би зрозуміти, чому і те, що ви вважаєте, є найкращою альтернативою.
NB Я спочатку задав це питання на Stack Overflow, але його було зупинено як головне на основі думки (само іронічно суб'єктивне) - сподіваємось, він може тут отримати більший успіх.
Вгорі оригінальний текст, нижче ще кілька думок, частково у відповідь на відповіді, отримані там, перш ніж він затримався:
- Одним із головних моментів відповідей був обсяг коду пластини котла, необхідний для вищезазначеної схеми, особливо коли потрібно багато таких типів. Однак на захист шаблону це може бути значною мірою автоматизовано за допомогою шаблонів, і насправді мені це все одно не здається поганим, але це лише моя думка.
- З концептуальної точки зору, чи не здається дивним при роботі з сильно набраною мовою, такою як C #, застосовувати лише сильно набраний принцип до складених значень, а не поширювати його на значення, які можуть бути представлені екземпляром вбудований тип?