I would like to implement a builder with a fluent interface.
To make things more difficult there are two additional requirements:
I would like the returned objects to be immutable so that it can be used as follows and the interface should be extendible in derived interfaces:
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
First an issue open for discussion: should the builder be immutable so that it has above semantics? (it's briefly commented on by Jonathan in How to create an immutable builder of an immutable class that contains a set?)
The builder interface should be extendible:
interface Builder<T extends Builder<T>> {
T setValue(int v);
}
interfacte BuilderEx<T extends BuilderEx<T>> {
T someOtherOpp();
}
(it uses self referencing generics as described by Eamonn McManus1)
To satisfy the first requirement AbstractBuilder's implementation is similar to the one in How to create an immutable builder of an immutable class that contains a set? :
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
should be similar to self()
1 and getThis()
2: converting a Builder<T>
to ConcreteBuilder
or ConcreteBuilderEx
depending on T
. The problem is how to implement castToConcrete
Since the builder methods create new instances the method of overriding getThis
in ConcreteBuilder
doesn't work. Using a "functor"3 supplied to the constructor of AbstractBuilder
and stored in a field enables conversion of Builder<T>
to T
. Add the following field (it uses the Guava library3 to AbstractBuilder
:
Function<Builder<T>, T> castToConcrete;
My current implementation of castToConcrete
turns quite ugly:
if-else
tree on instanceof
input instanceof Builder
and not input instanceof BuilderEx
a wrapper class which needs to define all methods in BuilderEx
, forward the ones in Builder
to input
and perform a default or nil operation for the others.input instanceof BuilderEx
it casts input to ConcreteBuilderEx
even though input
is probably not an actual instance of ConcreteBuilderEx
but an instance of an annonymous subclass of AbstractBuilderEx
(hoping that this works.)What is a better/cleaner way to do this?
The clean way is to not use an AbstractBuilder
but create a specific builder for each type you need to build. You get a fluent interface by making builder methods readable and customized to the object you are trying to build. If you need to create lots of builders you can use code generation, but the methods won't be as well designed.