I have an object hierarchy that increases in complexity as the inheritance tree deepens. None of these are abstract, hence, all of their instances serve a, more or less sophisticated, purpose.
As the number of parameters is quite high, I would want to use the Builder Pattern to set properties rather than code several constructors. As I need to cater to all permutations, leaf classes in my inheritance tree would have telescoping constructors.
I have browsed for an answer here when I hit some problems during my design. First of, let me give you a simple, shallow example to illustrate the problem.
public class Rabbit
{
public String sex;
public String name;
public Rabbit(Builder builder)
{
sex = builder.sex;
name = builder.name;
}
public static class Builder
{
protected String sex;
protected String name;
public Builder() { }
public Builder sex(String sex)
{
this.sex = sex;
return this;
}
public Builder name(String name)
{
this.name = name;
return this;
}
public Rabbit build()
{
return new Rabbit(this);
}
}
}
public class Lop extends Rabbit
{
public float earLength;
public String furColour;
public Lop(LopBuilder builder)
{
super(builder);
this.earLength = builder.earLength;
this.furColour = builder.furColour;
}
public static class LopBuilder extends Rabbit.Builder
{
protected float earLength;
protected String furColour;
public LopBuilder() { }
public Builder earLength(float length)
{
this.earLength = length;
return this;
}
public Builder furColour(String colour)
{
this.furColour = colour;
return this;
}
public Lop build()
{
return new Lop(this);
}
}
}
Now that we have some code to go on, imaging I want to build a Lop
:
Lop lop = new Lop.LopBuilder().furColour("Gray").name("Rabbit").earLength(4.6f);
This call will not compile as the last chained call cannot be resolved, Builder
not defining the method earLength
. So this way requires that all calls be chained in a specific order which is very impractical, especially with a deep hierarchy tree.
Now, during my search for an answer, I came across Subclassing a Java Builder class which suggests using the Curiously Recursive Generic Pattern. However, as my hierarchy does not contain an abstract class, this solution will not work for me. But the approach relies on abstraction and polymorphism to function which is why I don't believe I can adapt it to my needs.
An approach I have currently settled with is to override all methods of the superclass Builder
in the hierarchy and simply do the following:
public ConcreteBuilder someOverridenMethod(Object someParameter)
{
super(someParameter);
return this;
}
With this approach I can assure I am being returned an instance I can issue chain calls on. While this is not as worse as the Telescoping Anti-pattern, it is a close second and I consider it a bit "hacky".
Is there another solution to my problem that I am not aware of? Preferably a solution consistent with the design pattern. Thank you!
I did some experimenting and I found this to work quite nicely for me. Note that I prefer to create the actual instance at the start and the call all the setters on that instance. This is just a preference.
The main differences with the accepted answer is that
The code:
and then a subclass look like this:
and a subsub class
To verify it fully works I used this test:
If anyone still bumped into the same problem, I suggest the following solution, that conforms "Prefer composition over inheritance" design pattern.
Parent class
The main element of it is the interface that parent class Builder must implement:
Here is the changed parent class with the change:
The child class
The child class
Builder
must implement the same interface (with different generic type):Inside the child class
Builder
the field referencing parentBuilder
:this ensures that parent
Builder
methods are called in the child, however, their implementation is different:The constructor of Builder:
The constructor of builded child class:
Confronted with the same issue, I used the solution proposed by emcmanus at: https://community.oracle.com/blogs/emcmanus/2010/10/24/using-builder-pattern-subclasses
I'm just recopying his/her preferred solution here. Let say we have two classes,
Shape
andRectangle
.Rectangle
inherits fromShape
.public class Shape {
}
There is the
Init
inner class, which is abstract, and theBuilder
inner class, that is an actual implementation. Will be useful when implementing theRectangle
:public class Rectangle extends Shape { private final double height;
}
To instantiate the
Rectangle
:Again, an abstract
Init
class, inheriting fromShape.Init
, and aBuild
that is the actual implementation. EachBuilder
class implement theself
method, which is responsible to return a correctly cast version of itself.The following IEEE conference contribution Refined Fluent Builder in Java gives a comprehensive solution to the problem.
It dissects the original question into two sub-problems of inheritance deficiency and quasi invariance and shows how a solution to these two sub-problems opens for inheritance support with code reuse in the classical builder pattern in Java.
As you cannot use generics, now probably the main task is to somehow loosen typing. I don't know how you process those properties afterwards, but what if you used a HashMap for storing them as key-value pairs? So there will be just one set(key, value) wrapper method in the builder (or builder might not be necessary any more).
The downside would be additional type castings while processing the stored data.
If this case is too loose, then you could keep the existing properties, but have a general set method, which uses reflection and searches for setter method on the basis of 'key' name. Although I think reflection would be an overkill.
This form seems to nearly work. It is not very tidy but it looks like it avoids your issues:
Although I have successfully built
Rabbit
andLop
(in both forms) I cannot at this stage work out how to actually instantiate one of theBuilder
objects with it's full type.The essence of this method relies on the cast to
(B)
in theBuilder
methods. This allow you to define the type of object and the type of theBuilder
and retain that within the object while it is constructed.If anyone could work out the correct syntax for this (which is wrong) I would appreciate it.