いくつかのテクニックを組み合わせて頭を悩ませようとしています。
有効でない ValueObject を作成できないようにすることは、良い習慣のようです。そのための ValueObject コンストラクターは、提供されたコンテンツが有効な ValueObject を作成するのに十分でない場合は常に失敗します。私が持っている例では、値が存在する場合にのみ EmailAddress オブジェクトを作成できます。ここまでは順調ですね。
提供された電子メールアドレスの値を検証すると、私は原則を疑い始めます。4 つの例がありますが、どれがベスト プラクティスと見なされるべきかわかりません。
例 1 は簡単なものです。構成関数、必須パラメーター「値」、およびコードをクリーンに保つための別個の関数 validate だけです。すべての検証コードはクラス内にとどまり、外部からは決して利用できません。このクラスの目的は 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
}
例 2 は、検証関数を静的関数にします。関数はクラスの状態を決して変更しないため、これは 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 では、オブジェクトの本体内にハードコーディングされた別のクラスを紹介しています。もう 1 つのクラスは、検証コードを含む検証クラスであり、検証クラスが必要なときにいつでもどこでも使用できるクラスを作成します。クラス自体はハードコードされています。これは、その検証クラスへの依存関係を作成することも意味します。これは常に近くにある必要があり、依存性注入によって注入されません。バリデーターをハードコーディングするのは、完全なコードをオブジェクトに埋め込むのと同じくらい悪いと言う人もいるかもしれませんが、その一方で、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;
}
}