从历史上看,我在对象的构造函数中对对象进行了验证,并在验证失败时抛出异常。例如:
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;
}
}
虽然我喜欢这种方法,因为我的对象负责强制执行其一致性并确保它始终有效,但我从未对异常的使用过于热衷。虽然有人会争论或反对使用上述异常,但它确实限制了我在对多个对象执行验证时可以返回的验证消息的数量。例如:
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
}
}
应该两者Name
and 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();
}
}
对于以上的情况,我有几个顾虑:
- 如果验证失败,则仍会构造该对象,但其
$value
is null
这不是一个有效的状态。
- 创建后
Name
,我一定要记得打电话hasError
以确定是否发生验证错误。
- 我现在正在用以下方法来处理我的域对象
hasError
/getError
我不确定这是否是一个好的做法。
我是否遗漏了这个拼图的一块?我如何利用通知模式但确保我的对象不会进入无效状态?