Builder Design Pattern
Problem​
You: Hey Shubh, I need help with creating a Student
object. There are so many attributes. How can I handle this efficiently?
Shubh: Let me show you.
public class Student {
private String firstName;
private String fatherName;
private String motherName;
private String surname;
private int age;
private int rollNumber;
public Student(String firstName, String fatherName, String motherName, String surname, int age, int rollNumber){
this.firstName = firstName;
this.fatherName = fatherName;
this.motherName = motherName;
this.surname = surname;
this.age = age;
this.rollNumber = rollNumber;
}
}
You: So, to create a Student
object, I need to pass all the attributes. What if I don't have all the details at the time of creation, like the roll number?
Shubh: That's a good point. In such cases, you might think about using null for missing values or creating multiple constructors, but these solutions have their downsides.
Using NULL​
Imagine there are hundreds of attributes, which would require using NULL
.
Student student = new Student("Tom", NULL, NULL, NULL, NULL, NULL);
This approach leads to poor code quality and makes the code harder to maintain.
Multiple Constructors​
Another solution is creating different combinations of constructors to avoid NULL
values. However, this approach is not scalable due to the number of permutations and combinations required.
public class Student {
public Student() {}
public Student(String firstName, String surname, int age) {
//...
}
public Student(String firstName, String surname, int rollNumber) {
//...
}
public Student(String firstName, String fatherName, String surname, int age) {
//...
}
public Student(String firstName, String surname, int age, int rollNumber, String fatherName, String motherName) {
//...
}
//... more permutations and combinations
}
This problem is known as Constructor Explosion
, where many constructors are created to meet different requirements.
You: Using null isn't ideal, and creating many constructors seems messy.
Shubh: Exactly. This is where the Builder Design Pattern comes in handy. It helps to manage object creation with many attributes, making the code clean and scalable.
UML​
You: How does the Builder Design Pattern solve this problem?
Shubh: Let me explain the Builder Design Pattern with the help of a UML diagram.
In the Student
class (also known as Product
in general UML terms), we don't have to create various permutations and combinations of constructors. Instead, we just have a constructor that takes the builder, which has all the details like name, age, etc. Notice that all the properties are private, indicating that we can't modify the properties once the Student
object is created. The Student
object can only be created with the help of the builder since it is required by the constructor. Hence, the Builder Design Pattern can also help in creating immutable
objects.
Have you ever worked with StringBuilder in Java? It's immutable. How to achieve something like that? - Builder Design Pattern.
The abstract StudentBuilder
also has the same properties as the Student
class. It defines methods for setting the attributes of the Student
object. The key feature is that when we set the attributes, it returns the same Builder
object, allowing us to chain methods like .setName("xyz").setAge(23)
. It provides a method for building the final product (i.e., Student
). Once the final product is created, we can't modify it.
The abstract builder is often implemented as an interface or an abstract class. This allows for creating multiple concrete builders with different implementations if needed. In our example, we don't require concrete implementations, but in cases like building a computer, we can have ComputerBuilder
as an abstract class with concrete implementations like WindowsComputerBuilder
and MacComputerBuilder
.
Code​
I will be using fewer attributes for demonstration purposes.
Student Class​
public class Student {
private String name; // Notice that this is private
private int age;
private int rollNumber;
Student(StudentBuilder builder) {
this.name = builder.name;
this.age = builder.age;
this.rollNumber = builder.rollNumber;
}
}
StudentBuilder Class​
public class StudentBuilder {
protected String name;
protected int age;
protected int rollNumber;
public StudentBuilder setName(String name) {
this.name = name;
return this;
}
public StudentBuilder setAge(int age) {
this.age = age;
return this;
}
public StudentBuilder setRollNumber(int rollNumber) {
this.rollNumber = rollNumber;
return this;
}
public Student build() {
return new Student(this);
}
}
Main Class​
public class Main {
public static void main(String[] args) {
StudentBuilder builder = new StudentBuilder();
Student student = builder
.setName("Shubh")
.setAge(23)
.setRollNumber(56)
.build();
// student cannot be modified because we don't have setters in the Student class and all attributes are private.
System.out.println(student);
}
}
Key Benefits of Using the Builder Design Pattern​
Before watching, make sure you give it a try
-
Immutability: The Builder pattern helps create immutable objects by using private attributes and no setters in the final object class.
-
Method Chaining: The Builder pattern allows method chaining, making the code cleaner and more readable.
-
Reduced Complexity: The pattern avoids constructor explosion by using a builder to handle optional parameters, leading to more maintainable code.
-
Flexibility: Different concrete builders can be created to build variations of the product. This allows for different configurations of the same object type.
-
Scalability: Adding new attributes to a class becomes easier without affecting existing code. This makes the class scalable and easy to extend.