I know there are many variations and related topics to this one here on stack overflow but I haven't found any compelling answers so I'll give it a go myself.
I'm trying to design a builder factory that returns different subclasses of a common builder interface. I want to allow all the implementations to share a common abstract class for code re-use.
Note that I'm not interested in the return type of the build()
method, only what types the builders are.
This is what I have so far:
Builder interface with generic for the sub-interfaces:
interface FruitBuilder<T extends FruitBuilder<T>> {
T taste(String taste);
T shape(String shape);
T weight(String weight);
Fruit build();
}
Some builders have additional methods:
interface GrapesBuilder extends FruitBuilder<GrapeBuilder> {
GrapesBuilder clusterSize(int clusterSize);
}
Next is to specify a factory that returns the specific builders:
interface FruitBuilderFactory {
GrapesBuilder grapes();
AppleBuilder apple();
LemonBuilder lemon();
}
A user of these interfaces should be able to use it like:
Fruit grapes = fruitBuilderFactory
.grapes()
.weight(4)
.color("Purple")
.clusterSize(4) // Note that the GrapesBuilder type must be accessible here!
.build();
Most of the logic would go into the abstract class, including advanced build logic:
abstract class BaseFruitBuilder<T extends FruitBuilder<T>> implements FruitBuilder<T> {
String taste;
T taste(String taste) {
this.taste = taste;
return (T)this; // Ugly cast!!!!!
}
...
Fruit build() {
Fruit fruit = createSpecificInstance();
// Do a lot of stuff on the fruit instance.
return fruit;
}
protected abstract Fruit createSpecificInstance();
}
Given the base class, it's really simple to implement new builders:
class GrapseBuilderImpl extends BaseFruitBuilder<GrapesBuilder> {
int clusterSize;
GrapesBuilder clusterSize(int clusterSize) {
this.clusterSize = clusterSize;
}
protected Fruit createSpecificInstance() {
return new Grape(clusterSize);
}
}
This is all compiling and fine (at least my real code). The question if whether or not I can remove the ugly cast to T in the abstract class.