検証コードを配置できるオプションを見てみましょう。
- ビルダーのセッター内。
build()
メソッド内。
- 構築されたエンティティ内:
build()
エンティティが作成されるときにメソッドで呼び出されます。
オプション1を使用すると、問題をより早期に検出できますが、完全なコンテキストのみを持つ入力を検証できる複雑なケースがあるため、build()
メソッドの検証の少なくとも一部を実行できます。したがって、オプション1を選択すると、検証の一部が1つの場所で実行され、別の部分が別の場所で実行されるという矛盾したコードになります。
通常、ビルダーのセッターはbuild()
、特に流invokedなインターフェイスの直前に呼び出されるため、オプション2はオプション1よりもそれほど悪くはありません。したがって、ほとんどの場合、問題を早期に検出することは可能です。ただし、ビルダーがオブジェクトを作成する唯一の方法ではない場合、オブジェクトを作成するすべての場所でビルダーを使用する必要があるため、検証コードの重複につながります。この場合の最も論理的な解決策は、作成されたオブジェクトに可能な限り近い場所、つまり内部に検証を配置することです。そして、これがオプション3です。
SOLIDの観点からすると、ビルダーに検証を行うこともSRPに違反します。ビルダークラスは、データを集約してオブジェクトを構築する責任を既に負っています。検証は、独自の内部状態でコントラクトを確立することであり、別のオブジェクトの状態を確認することは新しい責任です。
したがって、私の観点からは、設計の観点から遅れて失敗する方が良いだけでなく、ビルダー自体ではなく、構築されたエンティティ内で失敗する方が良いです。
UPD:このコメントは、ビルダー(オプション1または2)内の検証が意味をなす場合、もう1つの可能性を思い出しました。ビルダーが、作成しているオブジェクトに独自のコントラクトを持っている場合、それは理にかなっています。たとえば、番号範囲のリストなど、特定のコンテンツを含む文字列を構築するビルダーがあるとします1-2,3-4,5-6
。このビルダーにはのようなメソッドがありaddRange(int min, int max)
ます。結果の文字列は、これらの数値について何も知りません。また、知る必要もありません。ビルダー自体は、文字列の形式と数値の制約を定義します。したがって、メソッドaddRange(int,int)
は入力数を検証し、maxがminより小さい場合に例外をスローする必要があります。
ただし、一般的なルールは、ビルダー自体によって定義された契約のみを検証することです。