質問
複雑な検証ロジックが必要なオブジェクトグラフの構築をどのように管理すればよいでしょうか。テスト容易性の理由から、依存性注入された何もしないコンストラクターを保持したいと思います。
テスト容易性は私にとって非常に重要ですが、コードのこの属性を維持するためにあなたの提案は何をしますか?
バックグラウンド
いくつかのビジネスデータの構造を管理するplain-old-java-objectがあります。
class Pojo
{
protected final String p;
public Pojo(String p) {
this.p = p;
}
}
このビジネスオブジェクトはその保証なしでは本当に意味がないので、pが有効な形式であることを確認したいと思います。pがナンセンスである場合でも、作成されるべきではありません。ただし、pの検証は自明ではありません。
キャッチ
実際には、ロジック自体が完全にテスト可能であるために十分に複雑な検証ロジックが必要であるため、そのロジックは別のクラスにあります。
final class BusinessLogic() implements Validator<String>
{
public String validate(String p) throws InvalidFoo, InvalidBar {
...
}
}
重複する可能性のある質問
- 検証ロジックはどこに実装する必要がありますか?-受け入れられた答えは私には突き通せません。「クラスのネイティブ環境で実行する」をトートロジーとして読みましたが、「クラスのネイティブ環境」以外で検証ルールを実行するにはどうすればよいですか?ポイント2グロクに失敗したので、コメントできません。
- 検証用のロジックルールをどこに提供しますか?-どちらの回答も、責任はクライアント/データプロバイダーにあることを示唆しています。これは原則として私が好きです。ただし、私の場合、クライアントはデータの発信者ではなく、データを検証できない可能性があります。
- 検証ロジックをどこに保持しますか?-検証はモデルが所有できることを示唆していますが、このアプローチはテストには理想的ではないと思います。具体的には、すべての単体テストで、モデルの他の部分をテストしているときでも、すべての検証ロジックに注意を払う必要があります。提案されたアプローチに従って、テストしたいものを完全に分離することはできません。
これまでの私の考え
次のコンストラクターはPojoの依存関係を公然と宣言し、その単純なテスト容易性を維持していますが、完全に安全ではありません。すべての入力が受け入れ可能であると主張するバリデーターをクライアントが提供することを妨げるものは何もありません!
public Pojo(Validator businessLogic, String p) throws InvalidFoo, InvalidBar {
this.p = businessLogic.validate(p);
}
したがって、コンストラクターの可視性をいくらか制限し、検証と構築を保証するファクトリメソッドを提供します。
@VisibleForTesting
Pojo(String p) {
this.p = p;
}
public static Pojo createPojo(String p) throws InvalidFoo, InvalidBar {
Validator businessLogic = new BusinessLogic();
businessLogic.validate(p);
return new Pojo(p);
}
これで、createPojoをファクトリクラスにリファクタリングできます。これにより、Pojoに「単一責任」が復元され、ファクトリロジックのテストが簡素化されます。もちろん、新しいPojoごとに新しい(ステートレス)BusinessLogicを無駄に作成しないというパフォーマンス上の利点もあります。
私の直感は、私はやめるべきだということです。外部の意見を求めてください。私は正しい方向に進んでいますか?