Избавляемся от фабрики сущностей, тесно связывая ее объекты-ценности

У меня есть мой User объекты значения сущности слабо связаны, и из-за этого я использую UserFactory создавать объект всякий раз, когда он поступает из базы данных или при создании совершенно нового объекта в домене.

Разве можно было бы плотно связать объекты-значения, чтобы я мог избавиться от его Фабрики, а мои службы приложений были бы раздуты с помощью индивидуальной логики создания объекта-значения (до того, как можно было внедрить их) при обновлении сущности и ее свойств? Разве объекты значения не имеют тесной связи с их корнем?

Например, когда я обновляю одно из свойств со слабосвязанной версией, мне сначала нужно будет создать экземпляр объекта значения, и затем впрысните это. Но в тесно связанном примере я мог бы просто вводить новые значения напрямую, без необходимости явно проходить процесс создания экземпляров VO.

Пример:

// Updating User's name (loosely coupled version)
$firstName = new FirstName('John');
$lastName  = new LastName('Doe');
$fullName  = new FullName($firstName, $lastName);

$user->setFullName($fullName);

// Updating User's name (tightly coupled version)
$user->setFullName('John', 'Doe');

Слабо связанный:

class User extends Entity
{
private $fullName;
private $email;

public function getFullName()
{
return $this->fullName;
}

public function setFullName(FullName $fullName)
{
$this->fullName = $fullName;

return $this;
}

public function getEmail()
{
return (string) $this->email;
}

public function setEmail(Email $email)
{
$this->email = $email;

return $this;
}

// etc.
}

Тесно связаны:

class User extends Entity
{
private $fullName;
private $email;

public function getFullName()
{
return $this->fullName;
}

public function setFullName($firstName, $lastName)
{
$firstName      = new FirstName($firstName);
$lastName       = new LastName($lastName);
$this->fullName = new FullName($firstName, $lastName);

return $this;
}

public function getEmail()
{
return (string) $this->email;
}

public function setEmail($email)
{
$this->email = new Email($email);

return $this;
}

// etc.
}

3

Решение

Я думаю, что пример очень упрощенный и не показывает истинную степень проблемы / вопроса. Я пытаюсь добавить больше сценариев, которые лучше продемонстрируют разницу между «слабо» & «тесно» связанные решения.

Использование сложного объекта-значения показывает, что создание объекта-значения не является обязанностью «установщика», поскольку для установки даты вам нужен языковой стандарт (или давайте представим другие значения — просто для демонстрации), а не только строковое значение даты. Таким образом, передача даты в качестве объекта значения имеет больше смысла и более четко демонстрирует намерение.

class User extends Entity
{

private $dateOfBirth;

public function setDateOfBirth(\Zend_Date $date)
{
$this->dateOfBirth= $date;
}

public function setDateOfBirth2($date = null, $part = null, $locale = null)
{
$date = new \Zend_Date($date, $part, $locale);

$this->dateOfBirth = $date;
}
}

Как вы можете видеть, метод User :: setDateOfBirth2 () выглядит неправильно — он имеет две обязанности и, следовательно, нарушает SRP. И в случае, если вам нужно установить дату с помощью объекта Zend_Date, вам нужно будет добавить другой метод. В следующем примере вы можете видеть, что установщик должен принимать только объект Value, а для создания «сложных» объектов Value вы можете создать вспомогательный (фабричный) метод или Factory — в зависимости от того, насколько он сложен:

class User extends Entity
{

private $dateOfBirth;

public function setDateOfBirth(\Zend_Date $date)
{
$this->date = $date;
}

public function createDate($date = null, $part = null, $locale = null)
{
return new \Zend_Date($date, $part, $locale);
}
}

$user = new User;

$user->setDateOfBirth($dateOfBirth);

// or

$user->setDateOfBirth($user->createDate($date, $part, $locale));
2

Другие решения

Других решений пока нет …