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

时间:2021-02-24 21:03:48

标签: php validation 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);
   }
}

1 个答案:

答案 0 :(得分:0)

值对象应始终在有效状态下实例化。无论验证是什么,您都应该在构造函数内部进行验证。

现在,什么构成有效性完全取决于您的域以及您打算如何使用这些对象。

也许不是专注于完全验证的 Password 对象,而是创建一个 UnvalidatedPassword 对象。它可以在内部设置诸如“必须是字符串,至少 8 个字符”等基本验证规则。并将此对象传递给服务进行进一步检查。

另一个选项是您的 Password 对象持有一个标志,表明它是否已经过外部验证。同样,对象应该处于有效的状态,不必经过域逻辑的全面审查。 (两种选择都是可行的!)

而且,有时值对象只是包装值并为它们提供上下文的好方法。他们并不总是需要完美的内部验证。将您的 UnvalidatedPassword 发送到最终返回 ValidPassword 对象的服务是另一个完全可以接受的选项。

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