Ответ 1
При моделировании объекта домена лучше всего учитывать реальные последствия. Скажем, вы имеете дело с объектом Employee
.
Сотрудникам требуется имя
Мы знаем, что в реальном мире у сотрудника всегда должно быть имя. Невозможно, чтобы сотрудник не имел имени. Другими словами, нельзя "построить" сотрудника без указания его имени. Итак, используйте параметризованные конструкторы! Мы также знаем, что имя сотрудника не может измениться - поэтому мы не допускаем этого, даже создавая частный сеттер. Использование системы типа .NET для проверки вашего сотрудника - очень сильная форма проверки.
public string Name { get; private set; }
public Employee(string name)
{
Name = name;
}
Допустимые имена имеют некоторые правила
Теперь он становится интересным. Имя имеет определенные правила. Давайте просто возьмем упрощенный маршрут и предположим, что допустимое имя - это не пустое или пустое. В приведенном выше примере кода следующее бизнес-правило не проверяется. На данный момент мы все еще можем создавать недопустимых сотрудников! Пусть это предотвратит это от EVER, изменив наш сеттер:
public string Name
{
get
{
return name;
}
private set
{
if (String.IsNullOrWhiteSpace(value))
{
throw new ArgumentOutOfRangeException("value", "Employee name cannot be an empty value");
}
name = value;
}
}
Лично я предпочитаю иметь эту логику в частном сеттере, чем в конструкторе. Сетчатка не полностью невидима. Сущность сама по себе может ее изменить, и мы должны обеспечить ее достоверность. Также всегда бросайте исключения!
Как разоблачить какой-либо метод IsValid()
?
Возьмите вышеуказанный объект Employee
. Где и как будет работать метод IsValid()
?
Вы позволили бы создать недействительный Employee, а затем ожидать, что разработчик проверит его достоверность с помощью проверки IsValid()
? Это слабый дизайн - прежде чем вы это узнаете, безымянные сотрудники будут путешествовать по вашей системе, нанося хаос.
Но, возможно, вы хотели бы разоблачить логику проверки имени?
Мы не хотим ловить исключения для потока управления. Исключение составляют катастрофические сбои системы. Мы также не хотим дублировать эти правила проверки в нашей кодовой базе. Поэтому, возможно, разоблачение этой логики проверки не является такой плохой идеей (но все же не самой большой!).
Что вы можете сделать, это предоставить статический метод IsValidName(string)
:
public static bool IsValidName(string name)
{
return (String.IsNullOrWhiteSpace(value))
}
Теперь наша собственность несколько изменилась:
public string Name
{
get
{
return name;
}
private set
{
if (!Employee.IsValidName(value))
{
throw new ArgumentOutOfRangeException("value", "Employee name cannot be an empty value");
}
name = value;
}
}
Но в этом дизайне есть что-то подозрительное...
Теперь мы начинаем внедрять методы проверки для отдельных свойств нашего объекта. Если свойство имеет всевозможные правила и поведение, связанные с ним, возможно, это знак того, что мы можем создать для него объект значения!
public PersonName : IEquatable<PersonName>
{
public string Name
{
get
{
return name;
}
private set
{
if (!PersonName.IsValid(value))
{
throw new ArgumentOutOfRangeException("value", "Person name cannot be an empty value");
}
name = value;
}
}
private PersonName(string name)
{
Name = name;
}
public static PersonName From(string name)
{
return new PersonName(name);
}
public static bool IsValid(string name)
{
return !String.IsNullOrWhiteSpace(value);
}
// Don't forget to override .Equals
}
Теперь наш объект Employee
может быть упрощен (я исключил проверку нулевой ссылки):
public Employee
{
public PersonName Name { get; private set; }
public Employee(PersonName name)
{
Name = name;
}
}
Наш код клиента теперь может выглядеть примерно так:
if(PersonName.IsValid(name))
{
employee = new Employee(PersonName.From(name));
}
else
{
// Send a validation message to the user or something
}
Итак, что мы здесь сделали?
Мы обеспечили постоянную согласованность нашей модели домена. Крайне важный. Невозможно создать недопустимый объект. Кроме того, мы использовали объекты ценности для обеспечения дальнейшего "богатства". PersonName
дал клиентскому коду больше контроля и большей мощности, а также упростил Employee
.