创建价值对象的最佳方式是什么?
What is the best way to create value objects?
我想知道如何以最好的方式创建值对象。
例如:
我有值对象密码
class Password
{
private string $password;
public function __construct(string $password)
{
$this->password = $password;
}
public function __toString(): string
{
return $this->password;
}
}
现在我想知道如何正确验证这一点。
VO 应始终具有正确的值。由于创建对象,我应该验证传递的值吗?
例如:
class Password
{
private string $password;
public function __construct(string $password)
{
$this->validate($password);
$this->password = $password;
}
public function __toString(): string
{
return $this->password;
}
private function validate(string $password): void
{
if(!validation_rule)
{
throw new InvalidPasswordException();
}
}
}
还是应该使用工厂?
class PasswordFactory implements PasswordFactoryInterface
{
private PasswordValidatorInterface $validator;
public function __construct(PasswordValidatorInterface $validator)
{
$this->validator = $validator;
}
public function createFromString(string $password): Password
{
$this->validator->validate($password);
return new Password(password_hash($password, PASSWORD_BCRYPT_DEFAULT_COST));
}
}
我的结论:
验证到期构造:
优势
- 我不会创建无效对象。
- 更少的代码。
缺点
- 我不能对外部验证器使用依赖注入。
工厂验证:
优势
- 我可以使用外部库通过依赖注入进行验证。
- 恕我直言,很容易测试。
- 密闭操作。如果我必须更改构造函数,我将仅更改工厂中的调用。
缺点
- 如果有人不使用工厂,我允许创建无效对象。
- 更多代码。
我还考虑过将验证器传递给命名构造函数
class Password
{
private string $password;
private function __construct(string $password)
{
$this->password = $password;
}
public static function createValidated(string $password, PasswordValidatorInterface $validator): self
{
$validator->validate($password);
return new self($password);
}
}
值对象应始终在有效状态下实例化。无论验证是什么,您都应该在构造函数中进行内部验证。
现在,什么 构成有效性完全取决于您的域以及您打算如何使用这些对象。
也许您不是专注于完全验证的 Password
对象,而是创建一个 UnvalidatedPassword
对象。它可以在内部设置规则,例如“必须是字符串,至少 8 个字符”和其他基本验证。并将此对象传递给服务以进行进一步检查。
另一种选择是您的 Password
对象持有一个标志,表明它是否已经过外部验证。同样,该对象应该处于有效的 状态 ,不一定要经过您的领域逻辑的全面审查。 (仍然是可行的选择!)
而且,有时值对象只是包装值并为它们提供上下文的好方法。他们并不总是需要 完美的内部验证。将您的 UnvalidatedPassword
发送到最终返回 ValidPassword
对象的服务是另一个完全可以接受的选择。
社区基于意见将其关闭是正确的;所有这些都是我的意见。但这是一个很酷的话题。
我想知道如何以最好的方式创建值对象。
例如:
我有值对象密码
class Password
{
private string $password;
public function __construct(string $password)
{
$this->password = $password;
}
public function __toString(): string
{
return $this->password;
}
}
现在我想知道如何正确验证这一点。
VO 应始终具有正确的值。由于创建对象,我应该验证传递的值吗?
例如:
class Password
{
private string $password;
public function __construct(string $password)
{
$this->validate($password);
$this->password = $password;
}
public function __toString(): string
{
return $this->password;
}
private function validate(string $password): void
{
if(!validation_rule)
{
throw new InvalidPasswordException();
}
}
}
还是应该使用工厂?
class PasswordFactory implements PasswordFactoryInterface
{
private PasswordValidatorInterface $validator;
public function __construct(PasswordValidatorInterface $validator)
{
$this->validator = $validator;
}
public function createFromString(string $password): Password
{
$this->validator->validate($password);
return new Password(password_hash($password, PASSWORD_BCRYPT_DEFAULT_COST));
}
}
我的结论:
验证到期构造:
优势
- 我不会创建无效对象。
- 更少的代码。
缺点
- 我不能对外部验证器使用依赖注入。
工厂验证:
优势
- 我可以使用外部库通过依赖注入进行验证。
- 恕我直言,很容易测试。
- 密闭操作。如果我必须更改构造函数,我将仅更改工厂中的调用。
缺点
- 如果有人不使用工厂,我允许创建无效对象。
- 更多代码。
我还考虑过将验证器传递给命名构造函数
class Password
{
private string $password;
private function __construct(string $password)
{
$this->password = $password;
}
public static function createValidated(string $password, PasswordValidatorInterface $validator): self
{
$validator->validate($password);
return new self($password);
}
}
值对象应始终在有效状态下实例化。无论验证是什么,您都应该在构造函数中进行内部验证。
现在,什么 构成有效性完全取决于您的域以及您打算如何使用这些对象。
也许您不是专注于完全验证的 Password
对象,而是创建一个 UnvalidatedPassword
对象。它可以在内部设置规则,例如“必须是字符串,至少 8 个字符”和其他基本验证。并将此对象传递给服务以进行进一步检查。
另一种选择是您的 Password
对象持有一个标志,表明它是否已经过外部验证。同样,该对象应该处于有效的 状态 ,不一定要经过您的领域逻辑的全面审查。 (仍然是可行的选择!)
而且,有时值对象只是包装值并为它们提供上下文的好方法。他们并不总是需要 完美的内部验证。将您的 UnvalidatedPassword
发送到最终返回 ValidPassword
对象的服务是另一个完全可以接受的选择。
社区基于意见将其关闭是正确的;所有这些都是我的意见。但这是一个很酷的话题。