I have a hierarchy of interfaces, with Child
implementing Parent
. I would like to work with immutable objects, so I would like to design Bui
Here is a solution that uses generics.
public abstract class ParentBuilder> {
private long parentProperty;
protected abstract T self();
public T withParentProperty(long parentProperty) {
this.parentProperty = parentProperty;
return self();
}
protected static class SimpleParent implements Parent {
private long parentProperty;
public SimpleParent(ParentBuilder> builder) {
this.parentProperty = builder.parentProperty;
}
@Override
public Long getParentProperty() {
return parentProperty;
}
}
}
public final class Child1Builder extends ParentBuilder {
private int child1Property;
private Child1Builder() {}
public static Child1Builder newChild1() {
return new Child1Builder();
}
@Override
protected Child1Builder self() {
return this;
}
public Child1Builder withChild1Property(int child1Property) {
this.child1Property = child1Property;
return self();
}
public Child1 build() {
return new SimpleChild1(this);
}
private final class SimpleChild1 extends SimpleParent implements Child1 {
private int child1Property;
public SimpleChild1(Child1Builder builder) {
super(builder);
this.child1Property = builder.child1Property;
}
@Override
public Integer getChild1Property() {
return child1Property;
}
}
}
public final class Child2Builder extends ParentBuilder {
private String child2propertyA;
private Object child2propertyB;
private Child2Builder() {}
public static Child2Builder newChild2() {
return new Child2Builder();
}
@Override
protected Child2Builder self() {
return this;
}
public Child2Builder withChild2PropertyA(String child2propertyA) {
this.child2propertyA = child2propertyA;
return self();
}
public Child2Builder withChild2PropertyB(Object child2propertyB) {
this.child2propertyB = child2propertyB;
return self();
}
public Child2 build() {
return new SimpleChild2(this);
}
private static final class SimpleChild2 extends SimpleParent implements Child2 {
private String child2propertyA;
private Object child2propertyB;
public SimpleChild2(Child2Builder builder) {
super(builder);
this.child2propertyA = builder.child2propertyA;
this.child2propertyB = builder.child2propertyB;
}
@Override
public String getChild2PropertyA() {
return child2propertyA;
}
@Override
public Object getChild2PropertyB() {
return child2propertyB;
}
}
}
For larger hierarchies or ones where concrete classes aren't just at the leaves, it is necessary to extract part of the above concrete builders into an intermediate abstract class. For instance, Child1Builder
could be split into the following two classes Child1Builder
and AbstractChild1Builder
, of which the latter could be extended by yet another child builder.
public abstract class AbstractChild1Builder> extends ParentBuilder {
protected int child1Property;
public T withChild1Property(int child1Property) {
this.child1Property = child1Property;
return self();
}
protected final class SimpleChild1 extends SimpleParent implements Child1 {
private int child1Property;
public SimpleChild1(AbstractChild1Builder builder) {
super(builder);
this.child1Property = builder.child1Property;
}
@Override
public Integer getChild1Property() {
return child1Property;
}
}
}
public final class Child1Builder extends AbstractChild1Builder {
private Child1Builder() {}
public static AbstractChild1Builder newChild1() {
return new Child1Builder();
}
@Override
protected Child1Builder self() {
return this;
}
public Child1 build() {
return new SimpleChild1(this);
}
}