I am trying to combine some methods.
It seems like good practice never allows you to create a ValueObject that is not valid. The ValueObject constructor should be interrupted whenever the content provided is not good enough to create a valid ValueObject. In the examples that I have, an EmailAddress object can only be created when a value is present. So far so good.
Checking the value of the emailaddress provided, where I start to doubt the principles. I have four examples, but I can’t say which one should be considered best practice.
Example 1 is simple: just a build function, the required parameter is “value”, and a separate function checks that the code is clean. All verification code remains inside the class and will never be available to the outside world. The class has only one purpose: to keep the email address and make sure that it will never be invalid. But the code will never be reused - I create an object with it, but that’s it.
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);
Example 2 makes the validate function a static function. A function will never change the state of a class, so this is the correct use of the static keyword, and the code in it will never be able to change anything in any instance created from a class that embeds a static function. But if I want to reuse the code, I can call a static function. However, it seems 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);
Example 3 represents another class hardcoded inside the body of my object. Another class is a verification class that contains verification code and thus creates a class that can be used whenever and wherever I need a verification class. The class itself is hardcoded, which also means that I am creating a dependency on this validation class, which should always be nearby and not injected through dependency injection. It can be said that having validator hard coding is as bad as the full code embedded in the object, but on the other hand: DI is important, and so you need to create a new class (extension or just rewrite) before just 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 validation class again, but places it in the constructor. Thus, My ValueObject needs an existing and created validator class before creating the class, but you can easily overwrite the validator. But how good it is for a simple ValueObject class to have such a dependency in the constructor, since the only thing that really matters is value, I should not care about how and where to contact if the letter is correct, and providing the correct validator.
public function __construct ($value, \Validator $validator) { if ( $validator->validate($value) ) { throw new \ValidationException('This is not an emailaddress.'); } $this->value = $value; }
In the last example, which I began to think about, a default validator is provided, but meanwhile it allows you to make rewriting for the validator in the constructor through DI. But I began to doubt how good a simple ValueObject is when you overwrite the most important part: validation.
So, everyone has an answer on how to best write this class, is it right for something as simple as an email address, or something more complicated like a barcode or visa card or something, oh what you might think t violates DDD, DI, OOP, DRY, misuse of static, etc ...
Full 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; } }