Обычно доменные объекты имеют свойства, которые могут быть представлены встроенным типом, но допустимые значения которых являются подмножеством значений, которые могут быть представлены этим типом.
В этих случаях значение может быть сохранено с использованием встроенного типа, но необходимо убедиться, что значения всегда проверяются в точке входа, в противном случае мы можем в итоге работать с недопустимым значением.
Одним из способов решения этой проблемы является сохранение значения как пользовательского, 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. Первоначально я задавал этот вопрос о переполнении стека, но он был отложен как основанный на мнениях (иронически субъективный сам по себе) - надеюсь, он сможет добиться большего успеха здесь.
Выше приведен исходный текст, ниже еще пара мыслей, частично в ответ на ответы, полученные до того, как он приостановился:
- Один из основных моментов, сделанных ответами, касался количества кода котельной плиты, необходимого для вышеуказанного шаблона, особенно когда требуется много таких типов. Однако в защиту паттерна это может быть в значительной степени автоматизировано с использованием шаблонов, и на самом деле мне все равно это не кажется слишком плохим, но это только мое мнение.
- С концептуальной точки зрения, не кажется ли странным при работе со строго типизированным языком, таким как C #, применять только строго типизированный принцип к составным значениям, а не распространять его на значения, которые могут быть представлены экземпляром встроенный тип?