流暢なインターフェースでビルダーを実装したいと思います。
要件
物事をより困難にするために、2つの追加要件があります。
返されたオブジェクトを不変にして、次のように使用できるようにし、インターフェイスを派生インターフェイスで拡張できるようにします。
ConcreteBuilder b1 = builder().setValue(1); ConcreteBuilder b2 = b1.setValue(2); ComplexObject o1 = b1.build(); o1.getValue(); // should return 1 ComplexObject o2 = b2.build(); o2.getValue(); // should return 2最初に議論の余地のある問題:ビルダーは、上記のセマンティクスを持つように不変である必要がありますか?( Jonathanが、セットを含む不変クラスの不変ビルダーを作成する方法について簡単にコメントしていますか? )
ビルダーインターフェイスは拡張可能である必要があります。
interface Builder<T extends Builder<T>> { T setValue(int v); } interfacte BuilderEx<T extends BuilderEx<T>> { T someOtherOpp(); }(Eamonn McManus 1で説明されているように、自己参照ジェネリックを使用します)
実装
最初の要件を満たすために、AbstractBuilderの実装は、セットを含む不変クラスの不変ビルダーを作成する方法の実装に似ています。:
class AbstractBuilder<T extends Builder<T>> implements Builder<T> {
T setValue(final int v) {
return castToConcrete(new AbstractBuilder<T>() {
// with build() overridden to use v
}
}
}
castToConcreteself()1およびgetThis()2に類似している必要があります:Builder<T>にConcreteBuilderまたはConcreteBuilderExに依存して変換しTます。問題は、どのように実装するかですcastToConcrete
Builderメソッドは新しいインスタンスを作成するため、オーバーライドするメソッドは機能getThisしConcreteBuilderません。のコンストラクターに提供され、フィールドに格納されている「ファンクター」3を使用すると、へAbstractBuilderの変換が可能になります。次のフィールドを追加します(Guavaライブラリ3を使用して:Builder<T>TAbstractBuilder
Function<Builder<T>, T> castToConcrete;
問題
私の現在の実装はcastToConcrete非常に醜いです:
- それは上の
if-else木ですinstanceof - のすべてのメソッドを定義する必要があるラッパークラスでは
input instanceof Builderなく、メソッドをに転送し、他のメソッドに対してデフォルトまたはnil操作を実行する場合。input instanceof BuilderExBuilderExBuilderinput の実際のインスタンスではなく、 (これが機能することを期待して)匿名のサブクラスのインスタンスであるにもかかわらず、[OPによる編集]機能しません:すべてのインターフェイスにラッパーが必要です[/ edit]input instanceof BuilderEx入力をキャストする場合ConcreteBuilderExinputConcreteBuilderExAbstractBuilderEx
これを行うためのより良い/よりクリーンな方法は何ですか?