创建价值对象的最佳方式是什么?

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));
    }
}

我的结论:

验证到期构造:

优势

  1. 我不会创建无效对象。
  2. 更少的代码。

缺点

  1. 我不能对外部验证器使用依赖注入。

工厂验证:

优势

  1. 我可以使用外部库通过依赖注入进行验证。
  2. 恕我直言,很容易测试。
  3. 密闭操作。如果我必须更改构造函数,我将仅更改工厂中的调用。

缺点

  1. 如果有人不使用工厂,我允许创建无效对象。
  2. 更多代码。

我还考虑过将验证器传递给命名构造函数

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 对象的服务是另一个完全可以接受的选择。

社区基于意见将其关闭是正确的;所有这些都是我的意见。但这是一个很酷的话题。