私のプロジェクトでは、DTOでいっぱいの2つのパッケージ、ゲッターとセッターだけのPOJOがあります。それらが単純なJavaBeanであることが重要ですが(たとえば、Apache CXFがそれらを使用してWebサービスXSDなどを作成するため)、そのようなプログラムはひどくエラーが発生しやすくなります。
Foo foo = new Foo();
foo.setBar("baz");
foo.setPhleem(123);
return foo;
私は流暢なインターフェースとビルダーオブジェクトを好むので、maven/gmavenを使用してDTOのビルダーを自動的に作成します。したがって、上記のコードの場合、aFooBuilder
が自動的に生成され、次のように使用できます。
Foo foo = new FooBuilder()
.bar("baz")
.phleem(123)
.build();
また、生成されたビルダーの単体テストも自動的に生成します。単体テストは、上記のコード(ビルダーバージョンと非ビルダーバージョン)の両方を生成し、両方のバージョンがとに関して同等であることを表明しequals()
ますhashcode()
。それを実現する方法は、すべてのプロパティタイプにデフォルトが設定されたグローバルにアクセス可能なマップを用意することです。このようなもの:
public final class Defaults{
private Defaults(){}
private static final Map<Class<?>, Object> DEFAULT_VALUES =
new HashMap<Class<?>, Object>();
static{
DEFAULT_VALUES.put(String.class, "baz");
// argh, autoboxing is necessary :-)
DEFAULT_VALUES.put(int.class, 123);
// etc. etc.
}
public static getPropertyValue(Class<?> type){
return DEFAULT_VALUES.get(type);
}
}
もう1つの重要な側面は、pojoにコレクションメンバーが存在する場合があることです。例えば:
foo.setBings(List<Bing> bings)
しかし、私のビルダーでは、この場合から2つのメソッドを生成したいと思います。setメソッドとaddメソッドです。
fooBuilder.bings(List<Bing> bings); // set method
fooBuilder.addBing(Bing bing); // add method
のプロパティフィールドにカスタムアノテーションを追加することでこれを解決しましたFoo
@ComponentType(Bing.class)
private List<Bing> bings;
ビルダービルダー(原文のまま)はアノテーションを読み取り、生成するメソッドのジェネリックタイプとして値を使用します。
私たちは今、質問に近づいています(申し訳ありませんが、簡潔さは私のメリットの1つではありません:-))。
このビルダーアプローチは複数のプロジェクトで使用できることに気付いたので、Mavenプラグインに変えることを考えています。私はMavenプラグインを生成する方法について完全に明確なので、それは質問の一部ではありません(また、有効なJavaソースコードを生成する方法でもありません)。私の問題は次のとおりです。一般的な依存関係(プロジェクトとプラグインの間)を導入せずに、上記の2つの問題に対処するにはどうすればよいですか。
<Question>
生成された単体テストのデフォルト値を取得するには、Defaultsクラス(または同様のメカニズム)が必要です(これは概念の重要な部分であり、完全にテストされていない場合、自動生成されたビルダーは信頼できません)。各プロジェクトには独自のドメインオブジェクトがあるので、この問題を解決するための優れた一般的な方法を考え出すのを手伝ってください。
ジェネリック型をビルダージェネレーターに伝達する一般的な方法が必要です。プロジェクトとプラグインの両方が同じアノテーションを認識する必要があるため、私が使用している現在のアノテーションベースのバージョンは満足のいくものではありません。
</Question>
何か案は?
ところで:ビルダーを使用することの本当の重要なポイントは、オブジェクトを不変にすることです。標準のJavaBeanが必要なため、私のものを不変にすることはできませんが、AspectJを使用して、ビルダー以外のコードベースのどこでもset-methodsもコンストラクターも呼び出されないように強制します。したがって、実用的な目的では、結果のオブジェクトは不変です。 。
また:はい、私は既存のBuilder-generatorIDEプラグインを知っています。それは私の目的に合わないので、自動化されたソリューションが必要です。これは、基盤となるコードが変更されたときに常に最新の状態になります。
Matt Bは、ビルダーの生成方法に関する情報を要求しました。これが私がすることです:
リフレクションごとにクラスを読み取り、Introspector.getBeanInfo(clazz).getPropertyDescriptors()
プロパティ記述子の配列を取得するために使用します。私のすべてのビルダーには、上記の場合の基本クラスAbstractBuilder<T>
がありT
ます。これがAbstractBuilderクラスのコードFoo
です。配列内のすべてのプロパティについて、プロパティの名前でメソッドが生成されます。これは、次の実装になります。PropertyDescriptor
FooBuilder.bar(String)
public FooBuilder bar(String bar){
setProperty("bar", bar);
return this;
}
このbuild()
メソッドはAbstractBuilder
、オブジェクトをインスタンス化し、そのプロパティマップ内のすべてのプロパティを割り当てます。