Простой вопрос, скажем, у меня есть email
значение объекта в моем домене, и в моем домене электронная почта может быть (нулевой, действительный адрес электронной почты), поэтому в этом случае, если я хочу передать объект электронной почты моему contact_info
Значение объекта, какой вариант, которые являются более действительными? // с точки зрения DDD
Опция 1:
class email{
public function construct($email)
{
if($email !== null)
{
// Assert that the email is valid
}
$this->email = $email
}
}
class contact_info{
public function __construct(Email $email){
}
}
Вариант 2:
class email{
public function construct($email)
{
// Assert that the email is valid
$this->email = $email
}
}
class contact_info{
public function __construct(Email $email = null){
if($email !== null){
$this->email = $email;
}
}
}
Очевидно, что вариант 1 намного лучше, и его абстракция имеет меньшую погрешность. но я новичок в DDD, так что я не уверен 🙂
Ваш первый вариант неверен, потому что класс имеет два возможных состояния и один недействителен, поскольку null
не может заполнить Email
интерфейс. Просто представьте, что вы используете экземпляр этого класса, вам нужно проверить, является ли электронная почта действительной, даже когда электронная почта не является обязательной. Это делает безопасность типов бессмысленной:
class Consumer{
// email is not optional at all
function __construct(Email $email){
if(!$email->isActuallyAnEmail()) // WTH?
throw new Exception;
}
}
То, что вы пытаетесь использовать, есть Шаблон NullObject. Но NullObjects работают лучше, если они используются вместо поведенческих интерфейсов; вместо этого вы моделируете скалярные данные, которые являются строкой электронной почты: шаблон NullObject можно использовать до тех пор, пока он будет работать и делать все отлично код вызывающего абонента. Если интерфейс должен где-то возвращать скалярные данные (в вашем случае это строка электронной почты), то NullObject, реализующий указанный интерфейс, может только создать эти данные. Так что это на самом деле не NullObject, а то, что я называю Поддельные / Пример / Заполнители объектов. Грубо говоря:
// behavioral-only, a perfectly fine NullObject
class NullLogger implements Logger{
function log($message){ return true; }
}
// this sucks. you are just converting null to errors
// or moving errors to another place
// this NullObject implementation is invalid
// as it doesn't actually fullfill the Email interface
class NullEmail implements Email{
function __toString(){ throw new Exception; }
}
// not behavioral-only, an ExampleObject in this case
class ExampleEmail implements Email{
function __toString(){ return "[email protected]"; }
}
И это, очевидно, имеет совершенно другое значение. Это больше не является обязательным значением. Данные есть, но они составлены; это пример значения, значение по умолчанию или просто заполнитель.
Таким образом, вариант 2 является правильным (если вы не в порядке с [email protected]
прикреплен к вашему объекту «контактная информация»).
Если вы хотите узнать больше о null
обработка, проверьте также Вариант выкройки / Может быть, монада что немного сложнее для понимания; Вы можете посмотреть на это, но это не обязательно.
У вас есть два объекта ValueObject: электронная почта и контактная информация, и оба могут иметь разные инварианты.
Ваш Email VO должен быть недействительным, если он имеет нулевое значение (Email (null) должен вызывать исключение). Тем не менее, Ваша контактная информация действительна, даже если адрес электронной почты не установлен.
По моему мнению, конструктору следует передавать только те значения, которые необходимы для правильности объекта, если электронная почта не нужна, не передавайте ее туда.
Конечно, аргументы по умолчанию делают вещи проще, но они часто скрывают истинное значение установки значения.
В этом примере это трудно увидеть, но посмотрите на это:
class Person:
def __init__(self, id, type=USER):
self._id = id
self._type = type
VS
class Person:
def __init__(self, id):
self._id = id
self._type = USER
def give_admin_permissions(self):
self._type = ADMIN
Мы могли бы передать тип ADMIN непосредственно конструктору, но действительно ли он говорит о том, что мы сделали? Это не слишком многословно.
Тем не менее, в вашем примере это хорошо.