I would like to implement a builder with a fluent interface.
Requirements
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 2First 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)
Implementation
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;
Problem
My current implementation of castToConcrete turns quite ugly:
- it’s an
if-elsetree oninstanceof - when
input instanceof Builderand notinput instanceof BuilderExa wrapper class which needs to define all methods inBuilderEx, forward the ones inBuildertoinputand perform a default or nil operation for the others. when[edit by OP]Doesn’t work: we need a wrapper for every interface[/edit]input instanceof BuilderExit casts input toConcreteBuilderExeven thoughinputis probably not an actual instance ofConcreteBuilderExbut an instance of an annonymous subclass ofAbstractBuilderEx(hoping that this works.)
What is a better/cleaner way to do this?
The clean way is to not use an
AbstractBuilderbut 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.