Reputation: 1090
I want to implement a builder pattern with static inner classes for lets say classes A with fields (a1, a2, a3), B with fields (b1, b2) and C with fields (c1), whereas all share fields (s1, s2) from super class SuperClass:
public class A extends SuperClass {
private final String a1;
...
private A(ABuilder builder) {
super(builder);
this.a1 = builder.a1;
...
}
public static class ABuilder extends SuperClassBuilder implements ABuilderInterface {
private String a1;
...
@Override
public ABuilder withA1(String a1) {
this.a1 = a1;
return this;
}
...
@Override
public SuperClass build() {
return new A(this);
}
}
}
Accordingly for B and C the builders just differ that they have their own fields and implement their own interfaces (BBuilderInterface and CBuilderInterface), whereas these interfaces are only defining which methods are to be implemented:
public interface ABuilderInterface extends SuperClassBuilderInterface {
ABuilderInterface withA1(String a1);
...
}
...<interfaces for B and C>
public interface SuperClassBuilderInterface {
SuperClassBuilderInterface withS1(String s1);
...
SuperClass build();
}
// Usage of the builders:
public SuperClass foo() {
return new A.ABuilder()
.withA1(...) // returns ABuilderInterface
...
.withS1(...) // returns SuperClassBuilderInterface
...
.build();
}
public abstract class SuperClass {
private final String s1;
...
protected SuperClass(SuperClassBuilder builder) {
this.s1 = builder.s1;
...
}
protected static abstract class SuperClassBuilder implements SuperClassBuilderInterface {
private String s1;
...
@Override
public SuperClassBuilder withS1(String s1) {
this.s1 = s1;
return this;
}
...
@Override
public abstract SuperClass build();
}
}
Now you can spot the limitation that when I use the builder I have to pay attention to call the with... methods related to the child class first, then chain the ones for the super class, which is not a big of a deal, but still not sure whether good practice. On the other side I could add the with... methods of the children classes to the super class interface all together and then the limitation is gone, but then I have an interface with mixed with... methods of different child classes.
Which one would you prefer/suggest?
Upvotes: 3
Views: 2512
Reputation: 29158
Modify the superclass's builder to use an F-bound (aka the Curiously Recurring Template Pattern).
public interface SuperClassBuilderInterface<SELF extends SuperClassBuilderInterface<SELF>> {
SELF withS1(String s1);
// etc.
Superclass build();
}
Then you have:
class SuperClassBuilder<SELF extends SuperClassBuilder<SELF>> implements SuperClassBuilderInterface<SELF>
interface ABuilderInterface<SELF extends ABuilderInterface<SELF>> extends SuperClassBuilderInterface<SELF>
class ABuilder extends SuperClassBuilder<ABuilder> implements ABuilderInterface<ABuilder>
Note that the implementation of SuperClassBuilder
must contain unchecked casts of the form return (SELF)this;
. The type system is theoretically powerful enough to not need this, but the resulting encoding would probably be very ugly (see this) and it's likely not worth it.
EDIT: This is what @shmosel meant
Upvotes: 5