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
I don't think getParent()
and getThis()
are necessary, if you're willing to accept the restriction that the withXXXProperty()
methods be called from "youngest" to "oldest":
class Parent
{
private final long parentProperty;
public long getParentProperty()
{
return parentProperty;
}
public static abstract class Builder
{
private long parentProperty;
public Builder withParentProperty( long parentProperty )
{
this.parentProperty = parentProperty;
return this;
}
public abstract T build();
}
public static Builder> builder()
{
return new Builder()
{
@Override
public Parent build()
{
return new Parent(this);
}
};
}
protected Parent( Builder> builder )
{
this.parentProperty = builder.parentProperty;
}
}
class Child1 extends Parent
{
private final int child1Property;
public int getChild1Property()
{
return child1Property;
}
public static abstract class Builder extends Parent.Builder
{
private int child1Property;
public Builder withChild1Property( int child1Property )
{
this.child1Property = child1Property;
return this;
}
public abstract T build();
}
public static Builder> builder()
{
return new Builder()
{
@Override
public Child1 build()
{
return new Child1(this);
}
};
}
protected Child1( Builder> builder )
{
super(builder);
this.child1Property = builder.child1Property;
}
}
class Child2 extends Parent
{
private final String child2PropertyA;
private final Object child2PropertyB;
public String getChild2PropertyA()
{
return child2PropertyA;
}
public Object getChild2PropertyB()
{
return child2PropertyB;
}
public static abstract class Builder extends Parent.Builder
{
private String child2PropertyA;
private Object child2PropertyB;
public Builder withChild2PropertyA( String child2PropertyA )
{
this.child2PropertyA = child2PropertyA;
return this;
}
public Builder withChild2PropertyB( Object child2PropertyB )
{
this.child2PropertyB = child2PropertyB;
return this;
}
}
public static Builder> builder()
{
return new Builder()
{
@Override
public Child2 build()
{
return new Child2(this);
}
};
}
protected Child2( Builder> builder )
{
super(builder);
this.child2PropertyA = builder.child2PropertyA;
this.child2PropertyB = builder.child2PropertyB;
}
}
class BuilderTest
{
public static void main( String[] args )
{
Child1 child1 = Child1.builder()
.withChild1Property(-3)
.withParentProperty(5L)
.build();
Child2 grandchild = Child2.builder()
.withChild2PropertyA("hello")
.withChild2PropertyB(new Object())
.withParentProperty(10L)
.build();
}
}
There's still some boilerplate here: the anonymous concrete Builder
in each builder()
method, and the super()
call in each constructor. (Note: that assumes that every level is designed for further inheritability. If at any point you have a final
descendant, you can make the builder class concrete and the constructor private.)
But I think this version is easier to follow, for the next programmer who comes along and has to maintain your code (no self-referential generics, for starters; a Builder
builds Xs
). And IMHO requiring the child properties to be set on the builder before the parent properties is as much an advantage, in terms of consistency, as it is a disadvantage in terms of flexibility.