I'm wondering how to in the best way create value objects.
For example:
I have value object Password
class Password
{
private string $password;
public function __construct(string $password)
{
$this->password = $password;
}
public function __toString(): string
{
return $this->password;
}
}
And now I wonder how to correctly validate this.
VO should always have the correct value. Should I validate the passed value due to creating an object?
For example:
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();
}
}
}
Or should I use the factory?
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));
}
}
My conclusions:
Validation due construction:
Advantages
- I will not create an invalid object.
- Less code.
Disadvantages
- I cannot use dependency injection for external validators.
Validation in factories:
Advantages
- I can use external libs for validations with Dependency Injection.
- Imho easy to test.
- Hermetized operation. If I have to change the constructor I will change invocations only in the factory.
Disadvantages
- I allow to create an invalid object if someone won't use the factory.
- More code.
I thought also about passing validator to named constructor
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);
}
}