流暢なインターフェースでビルダーを実装したいと思います。
要件
物事をより困難にするために、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
}
}
}
castToConcrete
self()
1およびgetThis()
2に類似している必要があります:Builder<T>
にConcreteBuilder
またはConcreteBuilderEx
に依存して変換しT
ます。問題は、どのように実装するかですcastToConcrete
Builderメソッドは新しいインスタンスを作成するため、オーバーライドするメソッドは機能getThis
しConcreteBuilder
ません。のコンストラクターに提供され、フィールドに格納されている「ファンクター」3を使用すると、へAbstractBuilder
の変換が可能になります。次のフィールドを追加します(Guavaライブラリ3を使用して:Builder<T>
T
AbstractBuilder
Function<Builder<T>, T> castToConcrete;
問題
私の現在の実装はcastToConcrete
非常に醜いです:
- それは上の
if-else
木ですinstanceof
- のすべてのメソッドを定義する必要があるラッパークラスでは
input instanceof Builder
なく、メソッドをに転送し、他のメソッドに対してデフォルトまたはnil操作を実行する場合。input instanceof BuilderEx
BuilderEx
Builder
input
の実際のインスタンスではなく、 (これが機能することを期待して)匿名のサブクラスのインスタンスであるにもかかわらず、[OPによる編集]機能しません:すべてのインターフェイスにラッパーが必要です[/ edit]input instanceof BuilderEx
入力をキャストする場合ConcreteBuilderEx
input
ConcreteBuilderEx
AbstractBuilderEx
これを行うためのより良い/よりクリーンな方法は何ですか?