我正在尝试结合一些技术。
永远不要创建无效的 ValueObject 似乎是一种很好的做法。每当提供的内容不足以创建有效的 ValueObject 时,ValueObject 构造函数就会失败。在我的示例中,只有存在值时才能创建 EmailAddress 对象。到目前为止,一切都很好。
验证所提供电子邮件地址的值,这就是我开始怀疑这些原则的地方。我有四个例子,但我不知道哪一个应该被认为是最佳实践。
示例 1 很简单:只是一个构造函数、一个必需的参数“值”和一个单独的函数 validate 以保持代码清洁。所有的验证码都保留在类中,并且永远不会被外界使用。该类只有一个目的:存储电子邮件地址,并确保它永远不会是无效的。但是代码永远不会可重用——我用它创建了一个对象,仅此而已。
public function __construct ($value)
{
if ( $this->validate($value) )
{
throw new \ValidationException('This is not an emailaddress.');
}
$this->value = $value;
}
protected function validate ($value)
{
return is_string($value); // Wrong function, just an example
}
示例 2 使 validate 函数成为静态函数。该函数永远不会改变类的状态,因此它是对 static 关键字的正确使用,并且其中的代码将永远无法对嵌入静态函数的类创建的任何实例进行任何更改。但是如果我想重用代码,我可以调用静态函数。不过,这对我来说感觉很脏。
public function __construct ($value)
{
if ( $self::validate($value) )
{
throw new \ValidationException('This is not an emailaddress.');
}
$this->value = $value;
}
public static function validate ($value)
{
return is_string($value); // Wrong function, just an example
}
示例 3 引入了另一个类,在我的对象主体中进行了硬编码。另一个类是一个验证类,包含验证代码,因此创建了一个可以在我需要验证类时随时随地使用的类。该类本身是硬编码的,这也意味着我在该验证类上创建了一个依赖项,它应该始终在附近,而不是通过依赖项注入来注入。可以说,硬编码验证器与将完整代码嵌入对象中一样糟糕,但另一方面:DI 很重要,这样就必须创建一个新类(扩展或简单地重写)以只需更改依赖项。
public function __construct ($value)
{
if ( $this->validate($value) )
{
throw new \ValidationException('This is not an emailaddress.');
}
$this->value = $value;
}
protected function validate ($value)
{
$validator = new \Validator();
return $validator->validate($value);
}
示例 4 再次使用验证器类,但将其放在构造函数中。因此,我的 ValueObject 需要在创建类之前已经存在和创建的验证器类,但可以轻松地覆盖验证器。但是对于一个简单的 ValueObject 类在构造函数中具有这样的依赖关系有多好,因为唯一真正重要的是值,如果电子邮件正确,我不应该知道如何以及在哪里处理,并提供一个正确的验证器。
public function __construct ($value, \Validator $validator)
{
if ( $validator->validate($value) )
{
throw new \ValidationException('This is not an emailaddress.');
}
$this->value = $value;
}
我开始考虑的最后一个示例是提供默认验证器,同时可以通过 DI 为构造函数中的验证器注入覆盖。但是当您覆盖最重要的部分:验证时,我开始怀疑一个简单的 ValueObject 有多好。
所以,任何人都有一个答案,应该以哪种方式编写这个类最好,这对于像电子邮件地址这样简单的东西,或者像条形码或签证卡这样更复杂的东西或任何人们可能想到的东西都是正确的,并且不违反 DDD , DI, OOP, DRY, 错误使用静态等等...
完整代码:
class EmailAddress implements \ValueObject
{
protected $value = null;
// --- --- --- Example 1
public function __construct ($value)
{
if ( $this->validate($value) )
{
throw new \ValidationException('This is not an emailaddress.');
}
$this->value = $value;
}
protected function validate ($value)
{
return is_string($value); // Wrong function, just an example
}
// --- --- --- Example 2
public function __construct ($value)
{
if ( $self::validate($value) )
{
throw new \ValidationException('This is not an emailaddress.');
}
$this->value = $value;
}
public static function validate ($value)
{
return is_string($value); // Wrong function, just an example
}
// --- --- --- Example 3
public function __construct ($value)
{
if ( $this->validate($value) )
{
throw new \ValidationException('This is not an emailaddress.');
}
$this->value = $value;
}
protected function validate ($value)
{
$validator = new \Validator();
return $validator->validate($value);
}
// --- --- --- Example 4
public function __construct ($value, \Validator $validator)
{
if ( $validator->validate($value) )
{
throw new \ValidationException('This is not an emailaddress.');
}
$this->value = $value;
}
}