What is the builder pattern for

Question from Abdul#4709

Stupid assignment requirement for using super method inside constructor

Just wanted to make sure prompt makes sense

System.out.print("Enter 'y' or 'n' if the triangle is filled: ");
char e = scanner.next().charAt(0);
boolean f = e == 'y' ? true : false;
Triangle triangle = new Triangle(a, b, c, d, f);

It's a small class so jus using var names like this

Once a constructor gets to 5 parameters it is probably time to switch to using the builder pattern.

It's boilerplate code in java, but it is a must for readability.

Can you share your triangle class as it is right now?

Never seen builder pattern before I will make sure to look it up

< CODE LOST TO TIME >

    protected Date dateCreated;

Thats...wierd. Why is this info stored?

I actually didn't make the Geometric object class it was posted by the prof

I made the other Triangle one

Man your prof is annoying

His lectures r even worse

Well, making do with what you have is...workable, but we can revisit that superclass to see how you would design it if you weren't a bored college professor.

Basically, for most uses the builder pattern is just a substitute for a language feature called named optional parameters

Consider this python

class Position:
    def __init__(self, *, x=0, y=0, z=0):
        self.x = x
        self.y = y
        self.z = z

What's the second parameter in that 🤔

It is a python shorthand saying "these things need to be named". That's not really the focus though.

With the constructor (init method - close enough for now) being written like this you can call it a bunch of different ways.

Position() # makes a 0, 0, 0
Position(x=1) # makes a 1, 0, 0
Position(z=4) # makes a 0, 0, 4
Position(y=2, z=4) # makes a 0, 2, 4
Position(x=1, y=2, z=4) # makes a 1, 2, 3

This has alot of cool benefits

Oh so you don't have to define different constructors many times unlike in java

For one, if a field has a sensible default value (like all of these do in the position case) you can just insert that if it isn't specified. Also, the parameters being named means that you can specify them "out of order" with the method/function definition, which is very important if you have more than 3 parameters.

"What does the 6th int mean" is a stupid question to have to ask yourself, not to mention the chance you get it wrong, so having the parameters named puts the name of the thing right next to the value.

And before I get to explaining the builder pattern (your way of hacking this language feature into java), try and consider how you would support the Position example with just overloading methods.

Aight

Perhaps having one method for just x and y and another one for x, y, z?

What if I want to specify just y and z?

That would require you to make another method

Which is repetition

And we just have three fields in a larger class there will be even more

Not only that, it wouldn't work.

Remember, if you have two methods with the same name (or constructors for that matter) then java needs to be able to tell them apart by the types of their arguments. So you can't have two constructors which both take two ints.

// As far as the compiler can tell, these are identical
Position(int x, int y) { ... }
Position(int y, int z) { ... }

You would have to start making static factory methods with different names for each case.

.positionYZ(...)
.positionXZ(...)

Ah shite true

And, while the default values piece of this is important, there will be times where mandating that the user name out all of the parameters, even if there are no optional parameters, makes it so your code is actually legible in the face of dozens of properties.

So without further ado - the builder pattern.

First, we will start with your triangle code

public class Triangle extends GeometricObject {
    private double a; // Side one.
    private double b; // Side two.
    private double c; // Side three.

    public Triangle() {
        this.setA(1.0);
        this.setB(1.0);
        this.setC(1.0);
    }

    public Triangle(double a, double b, double c, String d, boolean e) {
        super(d, e);
        this.setA(a);
        this.setB(b);
        this.setC(c);
    }

    public double getA() {
        return a;
    }

    public void setA(double a) {
        this.a = a;
    }

    public double getB() {
        return b;
    }

    public void setB(double b) {
        this.b = b;
    }

    public double getC() {
        return c;
    }

    public void setC(double c) {
        this.c = c;
    }

    public double getArea() {
        double p = (this.a + this.b + this.c) / 2;
        return Math.sqrt(p * (p - this.a) * (p - this.b) * (p - this.c));
    }

    public double getPerimeter() {
        return this.a + this.b + this.c;
    }

    @Override
    public String toString() {
        return "Triangle{" +
                "a=" + a +
                ", b=" + b +
                ", c=" + c +
                ", area=" + this.getArea() +
                ", perimeter=" + this.getPerimeter() +
                ", color='" + color + '\'' +
                ", filled=" + filled +
                '}';
    }
}

(kenndel#7506) Java is a beautiful language

public class Triangle extends GeometricObject {
    private double a; // Side one.
    private double b; // Side two.
    private double c; // Side three.

    public Triangle() {
        this.setA(1.0);
        this.setB(1.0);
        this.setC(1.0);
    }

    public Triangle(double a, double b, double c, String d, boolean e) {
        super(d, e);
        this.setA(a);
        this.setB(b);
        this.setC(c);
    }

    public double getA() {
        return a;
    }

    public double getB() {
        return b;
    }

    public double getC() {
        return c;
    }

    public double getArea() {
        double p = (this.a + this.b + this.c) / 2;
        return Math.sqrt(p * (p - this.a) * (p - this.b) * (p - this.c));
    }

    public double getPerimeter() {
        return this.a + this.b + this.c;
    }

    @Override
    public String toString() {
        return "Triangle{" +
                "a=" + a +
                ", b=" + b +
                ", c=" + c +
                ", area=" + this.getArea() +
                ", perimeter=" + this.getPerimeter() +
                ", color='" + color + '\'' +
                ", filled=" + filled +
                '}';
    }
}

Now lets give things more descriptive names and let's also get rid of that default constructor for now.

public class Triangle extends GeometricObject {
    private double sideA; // Side one.
    private double sideB; // Side two.
    private double sideC; // Side three.

    public Triangle(double sideA, double sideB, double sideC, String color, boolean filled) {
        super(color, filled);
        this.sideA = sideA;
        this.sideB = sideB;
        this.sideC = sideC;
    }

    public double getSideA() {
        return this.sideA;
    }

    public double getSideB() {
        return this.sideB;
    }

    public double getSideC() {
        return this.sideC;
    }

    public double getArea() {
        double p = (this.sideA + this.sideA + this.sideC) / 2;
        return Math.sqrt(p * (p - this.sideA) * (p - this.sideB) * (p - this.sideC));
    }

    public double getPerimeter() {
        return this.sideA + this.sideB + this.sideC;
    }

    @Override
    public String toString() {
        return "Triangle{" +
                "a=" + this.sideA +
                ", b=" + this.sideB +
                ", c=" + this.sideC +
                ", area=" + this.getArea() +
                ", perimeter=" + this.getPerimeter() +
                ", color='" + color + '\'' +
                ", filled=" + filled +
                '}';
    }
}

And for now on, I am going to leave out all of the methods for space

The first thing we want to do is make the constructor private, since we are going to be replacing the access pattern of "calling the constructor with all the arguments" with our builder.

public class Triangle extends GeometricObject {
    private double sideA; // Side one.
    private double sideB; // Side two.
    private double sideC; // Side three.

    private Triangle(double sideA, double sideB, double sideC, String color, boolean filled) {
        super(color, filled);
        this.sideA = sideA;
        this.sideB = sideB;
        this.sideC = sideC;
    }
}

Never seen a private constructor before

A private constructor can only be called from the class it is defined and stuff within that class, So lets make the stuff that goes in the class.

public class Triangle extends GeometricObject {
    private double sideA; // Side one.
    private double sideB; // Side two.
    private double sideC; // Side three.

    private Triangle(double sideA, double sideB, double sideC, String color, boolean filled) {
        super(color, filled);
        this.sideA = sideA;
        this.sideB = sideB;
        this.sideC = sideC;
    }

    public static class Builder {}
}

We add a builder class within the triangle class. This builder can access all of the private methods of the Triangle class because it is in the Triangle class.

Now this builder needs to keep track of all of the information needed to build a triangle. We also want that information to be added one piece at a time - one method call at a time.

Does it matter if Builder class is static or non static

Yep. Thats a confusing java specific thing - feel free to google why that is needed.

public class Triangle extends GeometricObject {
    private double sideA; // Side one.
    private double sideB; // Side two.
    private double sideC; // Side three.

    private Triangle(double sideA, double sideB, double sideC, String color, boolean filled) {
        super(color, filled);
        this.sideA = sideA;
        this.sideB = sideB;
        this.sideC = sideC;
    }

    public static class Builder {
        private double sideA;
        private double sideB;
        private double sideC;
        private String color;
        private boolean filled;

        public Builder() {
                // Put any defaults here. If there isn't any default set it to null and check for that later
               this.sideA = 1.0;
               this.sideB = 1.0;
               this.sideC = 1.0;
               this.color = "black";
               this.filled = true;
        }

        public void setSideA(double sideA) { this.sideA = sideA; }
        public void setSideB(double sideB) { this.sideB = sideB; }
        public void setSideC(double sideC) { this.sideC = sideC; }
        public void setColor(String color) { this.color = color; }
        public void setFilled(boolean filled) { this.filled = filled; }
    }
}

Now you have the ability to mutate the builder for whatever you want.

Wait, you have to redeclare the fields inside builder class?

usually, yeah. It is far from a perfect system.

It is bad code to write, but it provides the nicest possible outward facing interface.

So when you're mutating the fields inside builder does it update them in the main class also or just the builder class

Just the builder class. No instance of the outer class exists yet.

public class Triangle extends GeometricObject {
    private double sideA; // Side one.
    private double sideB; // Side two.
    private double sideC; // Side three.

    private Triangle(double sideA, double sideB, double sideC, String color, boolean filled) {
        super(color, filled);
        this.sideA = sideA;
        this.sideB = sideB;
        this.sideC = sideC;
    }

    public static class Builder {
        private double sideA;
        private double sideB;
        private double sideC;
        private String color;
        private boolean filled;

        public Builder() {
           // Put any defaults here. If there isn't any default set it to null and check for that later
            this.sideA = 1.0;
            this.sideB = 1.0;
            this.sideC = 1.0;
            this.color = "black";
            this.filled = true;
        }

        public void setSideA(double sideA) {
            this.sideA = sideA; 
        }

        public void setSideB(double sideB) { 
             this.sideB = sideB;
        }

        public void setSideC(double sideC) { 
             this.sideC = sideC;
        }

        public void setSideB(String color) { 
             this.color = color;
        }

        public void setFilled(boolean filled) { 
             this.filled = filled;
        }
    }
}

No change yet, I just cleaned up the above code.

So now that we can set the properties on the builder we have to add a method for actually constructing the Triangle.

public class Triangle extends GeometricObject {
    private double sideA; // Side one.
    private double sideB; // Side two.
    private double sideC; // Side three.

    private Triangle(double sideA, double sideB, double sideC, String color, boolean filled) {
        super(color, filled);
        this.sideA = sideA;
        this.sideB = sideB;
        this.sideC = sideC;
    }

    public static class Builder {
        private double sideA;
        private double sideB;
        private double sideC;
        private String color;
        private boolean filled;

        public Builder() {
           // Put any defaults here. If there isn't any default set it to null and check for that later
            this.sideA = 1.0;
            this.sideB = 1.0;
            this.sideC = 1.0;
            this.color = "black";
            this.filled = true;
        }

        public void setSideA(double sideA) {
            this.sideA = sideA; 
        }

        public void setSideB(double sideB) { 
             this.sideB = sideB;
        }

        public void setSideC(double sideC) { 
             this.sideC = sideC;
        }

        public void setColor(String color) { 
             this.color = color;
        }

        public void setFilled(boolean filled) { 
             this.filled = filled;
        }

        public Triangle build() {
             // You still need to remember the order, but it is only in one place at least
            // This is also the place you should put any validations. Checking if any required properties are not set, things are
           // set as null that should not be null, etc.
            return new Triangle(this.sideA, this.sideB, this.sideC, this.color, this.filled);
        }
    }
}

now, as it is written now, you would end up using the builder like this

Triangle.Builder builder = new Triangle.Builder();
builder.setSideA(...);
builder.setSideB(...);
builder.setSideC(...);
builder.setColor(...);
builder.setFilled(...);
Triangle triangle = builder.build();

One way to make it a bit easier to use the builder is to make each setter return a reference to the builder itself.

public class Triangle extends GeometricObject {
    private double sideA; // Side one.
    private double sideB; // Side two.
    private double sideC; // Side three.

    private Triangle(double sideA, double sideB, double sideC, String color, boolean filled) {
        super(color, filled);
        this.sideA = sideA;
        this.sideB = sideB;
        this.sideC = sideC;
    }

    public static class Builder {
        private double sideA;
        private double sideB;
        private double sideC;
        private String color;
        private boolean filled;

        public Builder() {
           // Put any defaults here. If there isn't any default set it to null and check for that later
            this.sideA = 1.0;
            this.sideB = 1.0;
            this.sideC = 1.0;
            this.color = "black";
            this.filled = true;
        }

        public Builder setSideA(double sideA) {
            this.sideA = sideA; 
             return this;
        }

        public Builder setSideB(double sideB) { 
             this.sideB = sideB;
             return this;
        }

        public Builder setSideC(double sideC) { 
             this.sideC = sideC;
             return this;
        }

        public Builder setColor(String color) { 
             this.color = color;
             return this;
        }

        public Builder setFilled(boolean filled) { 
             this.filled = filled;
             return this;
        }

        public Triangle build() {
            return new Triangle(this.sideA, this.sideB, this.sideC, this.color, this.filled);
        }
    }
}

This lets you "chain" the method calls like this

Triangle triangle = new Triangle.Builder()
    .setSideA(...)
    .setSideB(...)
    .setSideC(...)
    .setColor(...)
    .setFilled(...)
    .build();

So that is the basic "pattern". From here on out its kinda all preference and style.

Personally, I don't like the setThing naming with builders, so I usually choose to just use the field name. In a builder its kinda understood that you are setting things.

public class Triangle extends GeometricObject {
    private double sideA; // Side one.
    private double sideB; // Side two.
    private double sideC; // Side three.

    private Triangle(double sideA, double sideB, double sideC, String color, boolean filled) {
        super(color, filled);
        this.sideA = sideA;
        this.sideB = sideB;
        this.sideC = sideC;
    }

    public static class Builder {
        private double sideA;
        private double sideB;
        private double sideC;
        private String color;
        private boolean filled;

        public Builder() {
           // Put any defaults here. If there isn't any default set it to null and check for that later
            this.sideA = 1.0;
            this.sideB = 1.0;
            this.sideC = 1.0;
            this.color = "black";
            this.filled = true;
        }

        public Builder sideA(double sideA) {
            this.sideA = sideA; 
             return this;
        }

        public Builder sideB(double sideB) { 
             this.sideB = sideB;
             return this;
        }

        public Builder sideC(double sideC) { 
             this.sideC = sideC;
             return this;
        }

        public Builder color(String color) { 
             this.color = color;
             return this;
        }

        public Builder filled(boolean filled) { 
             this.filled = filled;
             return this;
        }

        public Triangle build() {
            return new Triangle(this.sideA, this.sideB, this.sideC, this.color, this.filled);
        }
    }
}
Triangle triangle = new Triangle.Builder()
    .sideA(...)
    .sideB(...)
    .sideC(...)
    .color(...)
    .filled(...)
    .build();

The other thing that is useful is to make the actual builder not constructable but instead give out an instance via a static method.

public class Triangle extends GeometricObject {
    private double sideA; // Side one.
    private double sideB; // Side two.
    private double sideC; // Side three.

    private Triangle(double sideA, double sideB, double sideC, String color, boolean filled) {
        super(color, filled);
        this.sideA = sideA;
        this.sideB = sideB;
        this.sideC = sideC;
    }

    // You get a new builder via Triangle.builder()
    public static Builder builder() {
        return new Builder();
    }

    public static class Builder {
        private double sideA;
        private double sideB;
        private double sideC;
        private String color;
        private boolean filled;

        // And now this is private so your users don't construct it directly
        private Builder() {
            this.sideA = 1.0;
            this.sideB = 1.0;
            this.sideC = 1.0;
            this.color = "black";
            this.filled = true;
        }

        public Builder sideA(double sideA) {
            this.sideA = sideA; 
             return this;
        }

        public Builder sideB(double sideB) { 
             this.sideB = sideB;
             return this;
        }

        public Builder sideC(double sideC) { 
             this.sideC = sideC;
             return this;
        }

        public Builder color(String color) { 
             this.color = color;
             return this;
        }

        public Builder filled(boolean filled) { 
             this.filled = filled;
             return this;
        }

        public Triangle build() {
            return new Triangle(this.sideA, this.sideB, this.sideC, this.color, this.filled);
        }
    }
}
Triangle triangle = Triangle.builder()
    .sideA(...)
    .sideB(...)
    .sideC(...)
    .color(...)
    .filled(...)
    .build();

I don't have a real rock solid argument for the .builder() static method other than it looks better, but I think I might have in the past.


<- Index