Builder Design pattern
I hope you guys read my introduction article of design pattern in java. If not then the link is available here Design Pattern in Java. This article does not totally depend on the previous article (Design Pattern in Java). But it has some basic understanding of the background of design patterns. Just remember the Builder design pattern belongs to the category of creational design pattern
Most of the time, we use builder objects that are created by using a builder design pattern, without knowing it. The builder Design pattern is used for handling the complex Constructors. What I mean by complex is that it has a large number of parameters. Yeah 😉. I know what you think… The setters and getters right?. But remember once we create an object(this object is written by using Builder Desing pattern) it needs to be immutable. So a bunch of setters are not so helpful. So once we write a class with the Builder Design pattern it guarantees that immutability of the object once its creation. 😋
A popular example of Java API
- StringBuilder
- DocumentBuilder
So let’s play with StringBuilder object
Alright, let’s see what is in here. We know that the String object is mutable. That means every time that you create a new string object in memory, which requires a new allocation of space for that new object. But here the append method is something else. It kind of build different immutable objects using the same object building process. Absolutely, and also there is any kind of objects(integer, boolean, etc) are available on the append method to the builder. So they are still immutable. As I already said, builder pattern helps us in creating immutable classes with a large set of state attributes 😋
The output
Let’s look at how to create a builder object
Before we go further, let’s see how to handle complex Constructors. So let’s assume that you are working on the computer parts shop and buyers are buying different parts. Your shop has
- Power Supply Unit
- motherboard
- CPU
- RAM
- Hard Drive
- Graphics card
- Sound card
- Speakers
- mouse
- monitor
- keyboard
with different manufacturers. As I mentioned, if we use the setter method for every attribute, it violates the immutability of the object once it created. So it is not really helpful. what if we are telescoping constructors ??? 🤔
> note:- telescoping constructors means that creating multiple constructors with each parameter variation.
well, well, well… Then how many constructors are there? we have 11 properties. Then there are 2¹¹ subsets. Wow… we have 2048 telescoping constructors with default one. So it is difficult to manage and absolutely it increases the file size. Builder pattern overcomes this problem by handing the constructor with an object rather than parameters. The builder typically is written with the static inner class because it returns the object that is built.
public class ComputerPart {
public static class Builder{
private String powerSupplyUnit;
private String motherboard;
private String cpu;
private String ram;
private String harddrive;
private String gpu;
private String soundCard;
private String speakers;
private String mouse;
private String monitor;
private String keyboard;
public Builder() { }
public ComputerPart build(){
return new ComputerPart(this);
}
public Builder powerSupplyUnit(String powerSupplyUnit){
this.powerSupplyUnit = powerSupplyUnit;
return this;
}
public Builder motherboard(String motherboard){
this.motherboard = motherboard;
return this;
}
public Builder cpu(String cpu){
this.cpu = cpu;
return this;
}
public Builder ram(String ram){
this.ram = ram;
return this;
}
public Builder harddrive(String harddrive){
this.harddrive = harddrive;
return this;
}
public Builder gpu(String gpu){
this.gpu = gpu;
return this;
}
public Builder soundCard(String soundCard){
this.soundCard = soundCard;
return this;
}
public Builder speakers(String speakers){
this.speakers = speakers;
return this;
}
public Builder mouse(String mouse){
this.mouse = mouse;
return this;
}
public Builder monitor(String monitor){
this.monitor = monitor;
return this;
}
public Builder keyboard(String keyboard){
this.keyboard = keyboard;
return this;
}
}
private final String powerSupplyUnit;
private final String motherboard;
private final String cpu;
private final String ram;
private final String harddrive;
private final String gpu;
private final String soundCard;
private final String speakers;
private final String mouse;
private final String monitor;
private final String keyboard;
public ComputerPart(Builder builder){
this.powerSupplyUnit = builder.powerSupplyUnit;
this.motherboard = builder.motherboard;
this.cpu = builder.cpu;
this.ram = builder.ram;
this.harddrive = builder.harddrive;
this.gpu = builder.gpu;
this.soundCard = builder.soundCard;
this.speakers = builder.speakers;
this.mouse = builder.mouse;
this.monitor = builder.monitor;
this.keyboard = builder.keyboard;
}
public String getPowerSupplyUnit() {
return powerSupplyUnit;
}
public String getMotherboard() {
return motherboard;
}
public String getCpu() {
return cpu;
}
public String getRam() {
return ram;
}
public String getHarddrive() {
return harddrive;
}
public String getGpu() {
return gpu;
}
public String getSoundCard() {
return soundCard;
}
public String getSpeakers() {
return speakers;
}
public String getMouse() {
return mouse;
}
public String getMonitor() {
return monitor;
}
public String getKeyboard() {
return keyboard;
}
@Override
public String toString() {
return "ComputerPart{" +
"powerSupplyUnit='" + powerSupplyUnit + '\'' +
", motherboard='" + motherboard + '\'' +
", cpu='" + cpu + '\'' +
", ram='" + ram + '\'' +
", harddrive='" + harddrive + '\'' +
", gpu='" + gpu + '\'' +
", soundCard='" + soundCard + '\'' +
", speakers='" + speakers + '\'' +
", mouse='" + mouse + '\'' +
", monitor='" + monitor + '\'' +
", keyboard='" + keyboard + '\'' +
'}';
}
}
Here you can notice that we have inner static class called Builder which helps us in building desired ComputerPart object with all mandatory attributes and combination of optional attributes, without losing the immutability.
Inside ComputerPart object, we have a constructor which takes the Builder object and assign the values of Builder static object. The method called ‘build’ which is inside the inner static builder class, is returning the ComputerPart instance that was created in inner static class(builder).
The main program
class Test{
public static void main(String[] args) {
ComputerPart user1 = new ComputerPart.Builder()
.gpu("GTX 1080")
.motherboard("ASUS TUF B365M PLUS GAMING WIFI")
.mouse("ASUS ROG Pugio P503")
.powerSupplyUnit("ASUS ROG-THOR-850P 850W")
.ram("CORSAIR VENGEANCE® RGB 16GB (2 X 8GB) 2666MHZ")
.cpu("AMD RYZEN™ 7 3700X")
.build();
System.out.println(user1);
ComputerPart user2 = new ComputerPart.Builder()
.ram("4TB WD Black 7200RPM")
.gpu("RTX 2060 6GB DDR6 Graphics Card")
.build();
System.out.println(user2);
}
}
In the main program, the user buys GPU, motherboard, mouse, power unit and Ram. And the user 2 buys ram and GPU. So that is how we handle the complex constructors using the Builder pattern.
>Note:- suppose, there is something that the user is always buying. Right…😉. In that case, you can set that attribute to the Builder constructor rather than creating the method which returns the Builder instance of that particular attribute.
I hope you guys understand the Builder design pattern ✌