I have a hierarchy of interfaces, with Child
implementing Parent
. I would like to work with immutable objects, so I would like to design Builder
classes that construct these objects conveniently. However, I have many Child
interfaces, and I don't want to repeat the code for building Parent
s in each type of child builder.
So, assume the following definitions:
public interface Parent {
public Long getParentProperty();
}
public interface Child1 extends Parent {
public Integer getChild1Property();
}
public interface Child2 extends Parent {
public String getChild2PropertyA();
public Object getChild2PropertyB();
}
How can I efficiently implement builders Child1Builder
and Child2Builder
? They should support operation like:
Child1 child1 = Child1Builder.newChild1().withChild1Property(5).withParentProperty(10L);
and
Child2 child2 = Child2Builder.newChild2().withChild2PropertyA("Hello").withParentProperty(10L).withChild2PropertyB(new Object());
I don't want to implement a special case of withParentProperty
for each child builder.
Edited to add second property to Child2
to clarify that this cannot be done with simple generics. I am not looking for a way to combine Child1
and Child2
- I am looking for a way to implement a Builder
system that does not duplicate the work of building the parent class for every child class.
Thanks for any help!
The solution I imagine is like the Curiously Recurring Template Pattern, or CRTP. You can define a base class to handle the parent-related initialization, but you still may find the two boilerplate getParent()
and getThis()
methods to be too much repetition in each derived child-related builder class.
Take a look:
abstract class ParentBase implements Parent
{
@Override
public final Long getParentProperty()
{
return parentProperty_;
}
protected void setParentProperty(Long value)
{
parentProperty_ = value;
}
private Long parentProperty_;
}
abstract class ParentBuilder<T extends ParentBuilder<T>>
{
T withParentProperty(Long value)
{
getParent().setParentProperty(value);
return getThis();
}
protected abstract ParentBase getParent();
protected abstract T getThis();
}
final class ConcreteChild1 extends ParentBase implements Child1
{
@Override
public Integer getChild1Property()
{
return childProperty_;
}
public void setChild1Property(Integer value)
{
childProperty_ = value;
}
private Integer childProperty_;
}
final class Child1Builder extends ParentBuilder<Child1Builder>
{
public Child1Builder()
{
pending_ = new ConcreteChild1();
}
public Child1Builder withChild1Property(Integer value)
{
pending_.setChild1Property(value);
return this;
}
@Override
protected ParentBase getParent()
{
return pending_;
}
@Override
protected Child1Builder getThis()
{
return this;
}
private final ConcreteChild1 pending_;
}
As you can see, the ParentBuilder
type expects to be cooperating with a derived type to allow it to return a properly-typed instance. Its own this
reference won't do, because the type of this
within ParentBuilder
is, of course, ParentBuilder
, and not, say, Child1Builder
as intended to maintain the "fluent" call chaining.
I owe the "getThis()
trick" to Angelika Langer's tutorial entry.