Unf*_*lux 6 php validation design-patterns domain-driven-design
从历史上看,我在对象的构造函数中对对象进行了验证,并在验证失败时抛出异常。例如:
class Name
{
const MIN_LENGTH = 1;
const MAX_LENGTH = 120;
private $value;
public function __construct(string $name)
{
if (!$this->isValidNameLength($name)) {
throw new InvalidArgumentException(
sprintf('The name must be between %d and %d characters long', self::MIN_LENGTH, self::MAX_LENGTH)
);
}
$this->value = $name;
}
public function changeName(string $name)
{
return new self($name);
}
private function isValidNameLength(string $name)
{
return strlen($name) >= self::MIN_LENGTH && strlen($name) <= self::MAX_LENGTH;
}
}
Run Code Online (Sandbox Code Playgroud)
虽然我喜欢这种方法,因为我的对象负责强制执行其一致性并确保它始终有效,但我从未对异常的使用过于热衷。虽然有人会争论或反对使用上述异常,但它确实限制了我在对多个对象执行验证时可以返回的验证消息的数量。例如:
class Room
{
private $name;
private $description;
public function __construct(Name $name, Description $description)
{
$this->name = $name;
$this->description = $description;
}
}
class Name
{
public function __construct(string $name)
{
// do some validation
}
}
class Description
{
public function __construct(string $description)
{
// do some validation
}
}
Run Code Online (Sandbox Code Playgroud)
如果 和 都Name验证Description失败,我希望能够返回两个对象的失败消息,而不仅仅是首先失败的对象的单个异常。
在阅读了一些通知模式后,我觉得这非常适合我的场景。我陷入困境的是如何执行验证并防止我的对象在验证失败时进入无效状态。
class Name
{
const MIN_LENGTH = 1;
const MAX_LENGTH = 120;
private $notification;
private $value;
public function __construct(string $name, Notification $notification)
{
$this->notification = $notification;
$this->setName($name);
}
private function setName(string $name)
{
if ($this->isValidNameLength($name)) {
$this->value = $name;
}
}
private function isValidNameLength(string $name)
{
if (strlen($name) < self::MIN_LENGTH || strlen($name) > self::MAX_LENGTH) {
$this->notification->addError('NAME_LENGTH_INVALID');
return false;
}
return true;
}
public function hasError()
{
return $this->notification->hasError();
}
public function getError()
{
return $this->notification->getError();
}
}
Run Code Online (Sandbox Code Playgroud)
对于以上的情况,我有几个顾虑:
$value不是null有效的。Name,我必须记得调用hasError以确定是否发生验证错误。hasError/getError函数来处理我的域对象,我不确定这是否是一个好的做法。我是否遗漏了这个拼图的一块?我如何利用通知模式但确保我的对象不会进入无效状态?