Skip to main content

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.

Student.java
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.

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.

Multiple Constructors
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.

UML for Builder Design Pattern UML

Builder Design Pattern UML

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.

Note

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​

Student.java
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​

StudentBuilder.java
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​

Main.java
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.