Builder Pattern — A Quick Guide
Design Patterns: Builder Pattern
--
“Builder Pattern helps us construct complex objects by allowing creation of different representations of that object using same construction code.”
Builder Pattern is just a simple alternative of telescoping constructors and
many lines of setters.
public class Pizza { private int slices;
private boolean cheese;
private boolean mushrooms; public Pizza(int slices) { … }
public Pizza(int slices, boolean cheese) { … }
public Pizza(int slices, boolean cheese, boolean mushrooms) { … } // implement methods }
So this is what a telescoping constructor look like.
We can extend it even more:
public Pizza(int slices, boolean cheese, boolean mushrooms, boolean pepperoni, boolean onions, boolean studentDiscount…) { … }
Assuming slices is a required parameter and the rest of them is not,
it wouldn’t be wise to create a different constructor for each optional field to satisfy all object combinations.
Simplest way to get rid of this would be to just create a constructor which takes slice parameter and sets rest of the fields upon our needs.
public class Pizza { private int slices;
private boolean cheese;
private boolean mushrooms; public Pizza(int slices) { … } }
And the client code would be like:
Pizza pizza = new Pizza(2);pizza.setCheese(true);
pizza.setMushrooms(true);
pizza.setPepperoni(true);
…
This alternative doesn’t look good either.
The easiest way to save this code is to make a builder class, such as:
public class Pizza {
private int slices;
private boolean cheese;
private boolean mushrooms;
private boolean pepperoni;
private boolean onions;
private boolean studentDiscount;
public Pizza(Builder builder) {
this.slices = builder.slices;
this.cheese = builder.cheese;
this.mushrooms = builder.mushrooms;
this.pepperoni = builder.pepperoni;
this.onions = builder.onions;
this.studentDiscount = builder.studentDiscount;
}
// getters & setters
public static final class Builder {
private int slices;
private boolean cheese;
private boolean mushrooms;
private boolean pepperoni;
private boolean onions;
private boolean studentDiscount;
private Builder() {}
public static Builder initialize(int slices) {
return new Builder().withSlices(slices);
}
public Builder withSlices(int slices) {
this.slices = slices;
return this;
}
public Builder withCheese() {
this.cheese = true;
return this;
}
public Builder withMushrooms() {
this.mushrooms = true;
return this;
}
public Builder withPepperoni() {
this.pepperoni = true;
return this;
}
public Builder withOnions() {
this.onions = true;
return this;
}
public Builder withStudentDiscount() {
this.studentDiscount = true;
return this;
}
public Pizza build() {
return new Pizza(this);
}
}
}
Since slices is a required field, Builder’s private constructor is hidden and has a simple factory method that only permits to initialize with slices.
Up until build() method is called, Builder returns a Builder type,
build() method transforms Pizza.Builder to an actual Pizza object.
And the client code would be like:
Pizza pizza = Pizza.Builder
.initialize(2)
.withCheese()
.withMushrooms()
.withStudentDiscount()
.build();
Voila!
Thank you for reading and being a part of my journey!
Reference(s)
- Dive into Design Patterns. by Alexander Shvets, Released 2019.