I'm trying to get my head around combining some techniques.
It seems good practice to never make it possible to create a ValueObject that is not valid. The ValueObject constructor therefor should fail whenever the provided content is not good enough to create a valid ValueObject. In the examples I have, an EmailAddress object can only be created when there is a value present. So far, so good.
Validating the value of the provided emailaddress, that's where I begin to doubt the principles. I have four examples, but I can't tell which one should be considered the best practice.
Example 1 is the easy one: simply a construct function, a required parameter "value", and a separate function validate to keep the code clean. All the validation code stays inside the class, and will never be available to the outside world. The class has only one purpose: store the emailaddress, and make sure it will never be an invalid one. But the code will never be reusable - I create an object with it, but that's all.
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 makes the validate function a static function. The function will never change the state of the class, so it is a correct use of the static keyword, and the code in it will never be able to change anything to any instance created from the class embedding the static function. But if I want to reuse the code, I can call the static function. Still, this feels dirty to me.
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 introduces another class, hardcoded inside the body of my object. The other class is a validation class, containing the validation code, and creates thus a class that can be used whenever and wherever I need a validation class. The class itself is hardcoded, which also means that I create a dependency on that validation class, which should be always nearby, and is not injected through dependency injection. One could say that having a validator hard coded is as bad as having the complete code embedded in the object, but on the other hand: DI is important, and this way one has to create a new class (extending, or simply rewriting) to simply change the dependency.
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 uses the validator class again, but puts it in the constructor. My ValueObject thus needs a validator class already present and created, before creating the class, but it is possible to easily overwrite the validator. But how good is it for a simple ValueObject class to have such a dependency in the constructor, as the only thing really important is the value, it should not be my concern to know how and where to handle if the email is correct, and providing a correct validator.
public function __construct ($value, \Validator $validator)
{
if ( $validator->validate($value) )
{
throw new \ValidationException('This is not an emailaddress.');
}
$this->value = $value;
}
The last example I started thinking about, is providing a default validator, and meanwhile make it possible to inject through DI an overwrite for the validator in the constructor. But I started doubting how good a simple ValueObject is when you overwrite the most important part: the validation.
So, anyone has an answer which way one should best write this class, that is correct for something as easy as an emailaddress, or something more complex like a barcode or a visa card or whatever one may think about, and doesn't violate DDD, DI, OOP, DRY, wrong use of static, and so on...
The complete code:
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;
}
}
Email
class itself already. If you find yourself having to use the "validation code" from outside that class then you are likely doing something wrong. – Lope