0

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?

Community
  • 1
  • 1

1 Answers1

0

Your first example is not bad, but I don't think it is what you are looking for.

I am still a little unsure of exactly what you want, but seeing your examples do not work for you, I thought I'd give you one or two of my own. :)

class ParentBuilder{
    public ConcreteParent build(){
        ConcreteParent parent = new ConcreteParent();
        parent.setFirst(1);
        parent.setSecond(2);
        parent.setThird(3);
        return parent;
    }
}

class ChildBuilder{
    public ConcreteChild build(ParentBuilder parentBuilder){
        ConcreteParent parent = parentBuilder.build();
        ConcreteChild child = new ConcreteChild();
        child.setFirst(parent.getFirst());
        child.setSecond(parent.getSecond());
        child.setThird(parent.getThird());
        child.setFourth(4); //Child specific value
        child.setFifth(5); //Child specific value
        return child;
    }
}

Any new type, would have its own builder, taking in its parent's builder. As you can see this is similar to:

        public NutritionFacts build() { return new NutritionFacts(this); }
    }

    protected NutritionFacts(Builder builder) {
        calories = builder.calories;
    }

In your example.

This however, quickly gets out of hand as well, increasingly for the number of variables and subclasses.

An alternativ, would be to use dynanic variables, have a look at this: http://martinfowler.com/apsupp/properties.pdf Martin Fowler writes a great article specifying all the pros and cons.

Anyways, here's my second example:

public class Demo {

    public static void main(String[] args) {
        ConcreteBuilder builder = new ConcreteBuilder();
        Concrete concrete = builder.with("fourth", "valueOfFourth").build();
        for(String value : concrete.getAttributes().values())
            System.out.println(value);
    }
}

class ConcreteBuilder{
    private Concrete concrete;

    public ConcreteBuilder(){
        concrete = new Concrete();
    }

    public ConcreteBuilder with(String key, String value){
        concrete.getAttributes().put(key, value);
        return this;
    }
    public Concrete build(){
        return concrete;
    }
}

class Concrete{
    private HashMap<String, String> attributes;

    public Concrete(){
        attributes = new HashMap<>();
    }

    public HashMap<String, String> getAttributes(){
        attributes.put("first", "valueOfFirst");
        attributes.put("second", "valueOfSecond");
        attributes.put("third", "valueOfThird");
        return attributes;
    }
}

The magic here is, you (might) no longer need all these subclasses. If these subclasses' behavior does not change, but only their variables, you should be fine using a system like this. I strongly advise that you read Martin Fowler article on the subject though, there are good places and bad places to do this, but I think this is a good one.

I hope this brings you closer to an answer, good luck. :)

Chris Wohlert
  • 610
  • 3
  • 12
  • I know this is a little late, but that you for your answer! My supervisor at the time told me to avoid the builder pattern completely because Beans are required to have getter and setter methods. I was naive and hoping to implement a "clever" solution. – maester999 Nov 30 '17 at 14:01