I would like to use the builder pattern in some upcoming work that I have which has several classes in a hierarchy. The base class will have at least 9 fields to start, and the various sub-classes may add between 2-4 more fields each. This would get out of hand very quickly and the builder pattern is appealing to me for this exact reason. I got some initial exposure to the builder pattern in books and articles. They were helpful, but had nothing on how to extend this pattern. I tried to implement this by myself, but I ran into trouble with the constructors of each of the sub-classes because I didn't get how to pass the collected data in the builder to super class. I looked on SO for some answers, and here's what I found.
This one is from SO 24243240 where an example of how to extend an abstract class with an abstract builder is given. It is also based on this blog post.
public abstract class AbstractA {
protected String s;
protected int i;
protected AbstractA() {
}
protected abstract static class ABuilder<T extends AbstractA, B extends ABuilder<T,B>> {
protected T object;
protected B thisObject;
protected abstract T getObject(); //Each concrete implementing subclass overrides this so that T becomes an object of the concrete subclass
protected abstract B thisObject(); //Each concrete implementing subclass builder overrides this for the same reason, but for B for the builder
protected ABuilder() {
object = getObject();
thisObject = thisObject();
}
public B withS(String s) {
object.s = s;
return thisObject;
}
public B withI(int i) {
object.i = i;
return thisObject;
}
public T build() {
return object;
}
}
}
public final class ConcreteA extends AbstractA {
private String foo;
protected ConcreteA() {
}
public static final class Builder extends AbstractA.ABuilder<ConcreteA,Builder> {
@Override protected ConcreteA getObject() {
return new ConcreteA();
}
@Override protected Builder thisObject() {
return this;
}
public Builder() {
}
public Builder withFoo(String foo) {
object.foo = foo;
return this;
}
}
}
And then in client code, it would look like...
ConcreteA baz = new ConcreteA.Builder().withFoo("foo").withS("bar").withI(0).build();
I like this example because it allows you to easily extend these classes, but it also seems to me that this defeats the purpose of using the builder pattern because the methods withS(String s)
and withI(int i)
act alot like setter methods. Also, this method leaves the fields of the base class and the builder class as protected rather than private.
Here's one from SO 17164375
public class NutritionFacts {
private final int calories;
public static class Builder<T extends Builder> {
private int calories = 0;
public Builder() {}
public T calories(int val) {
calories = val;
return (T) this;
}
public NutritionFacts build() { return new NutritionFacts(this); }
}
protected NutritionFacts(Builder builder) {
calories = builder.calories;
}
}
public class GMOFacts extends NutritionFacts {
private final boolean hasGMO;
public static class Builder extends NutritionFacts.Builder<Builder> {
private boolean hasGMO = false;
public Builder() {}
public Builder GMO(boolean val) {
hasGMO = val;
return this;
}
public GMOFacts build() { return new GMOFacts(this); }
}
protected GMOFacts(Builder builder) {
super(builder);
hasGMO = builder.hasGMO;
}
}
I like that this one seemingly adheres more closely to the builder pattern described by Josh Bloch and it also allows you to simply pass the builder into the constructor for the class you want to instantiate. This would be a nice way to do some validation inside the builder before instantiating the object in the call to build()
. At the same time though, this example shows how you can extend the builder pattern with concrete classes, and when you do that the potential for all the nastiness that comes with extending concrete classes (e.g. inconsistent interfaces, inheriting methods which can corrupt the state of your object, etc.)
So my question is there a way to implement an abstract class with an abstract builder that also allows you to pass in a reference to a builder in the constructor for the base class? Something like:
public abstract BaseClass {
// various fields go here
...
public abstract Builder<T extends BaseClass, B extends Builder<T,B>> {
// add chaining methods here
...
public T build() {
if (isValid()) return new T(this);
else Throw new IllegalArgumentException("Invalid data passed to builder.");
}
}
public BaseClass(Builder builder) {
// set fields of baseclass here
}
}
I realize that you can't instantiate an object the way that I've shown here, but is there some other way to do it I mean? Is this possibly where a factory would go? Maybe I just have the wrong assumptions about the builder pattern in general. :) If that's the case, is there a better direction to take?