Builder design pattern in Java

By | August 30, 2023

The Builder design pattern is a creational design pattern that is used to create the objects (complex objects) step by step and then finally return the final object with the required attribute values. It allows us to create different types of objects using different attributes, depending on our requirements.

Why to use Builder design pattern

Let us understand why we need the Builder design pattern. As we already have Factory design patterns and Abstract factory design patterns to create objects. There are mainly three major problems with factory design patterns:

  • When we need to pass a large number of arguments to create the objects. It becomes difficult to maintain the large number of arguments and their order in the constructor.
  • If a few parameters are optional, in the factory design pattern, we need to send all of those parameters, and for optional parameters, we need to send null values.
  • If the object is very heavy and complex, then all the complexity needs to be taken care of by the factory classes, and that makes it difficult to manage.

Example

In this example, we will learn the builder design pattern with the help of the employee class. We will have multiple fields for employees, such as empId, empName, empAddress, empEmail, empPassword, empGender, empSalary, etc. As we have a large number of arguments, in the factory design pattern, to create an employee object, we need to pass all of these parameters. Instead, we use Builder Design and will create the employee object step by step by passing only the required fields.

Step by Step implementation of Builder design pattern in Java

Step 1- Create Employee class

First, we create an Employee class and define all the member variables of the employee class. We make its constructor private so that no one can create the object using constructor initialization. We create only getter methods, not setters, as we do not want anyone to set values to their fields.

Employee.java
/**
 * Builder Design Pattern
 * 
 * @author paulsofts
 * */

public class Employee {
	
	private final int empId;
	private final String empName;
	private final String empAddress;
	private final String empEmailId;
	private final String empPassword;
	private final char empGender;
	private final double empSalary;
	
	/**
	 * We create a private constructor. So, that anyone
	 * can not create the object of class using constructor
	 * initialization
	 * 
	 * @author paulsofts
	 * */
	
	private Employee() {
		
	}
	
	/**
	 * 1. We create only getter methods for Employee class
	 * 2. Not setter methods, as we do not want anyone to
	 * 	  set the data to employee object
	 * 
	 * @author paulsofts
	 * */
		
	public int getEmpId() {
		return empId;
	}
	
	public String getEmpName() {
		return empName;
	}
	
	public String getEmpAddress() {
		return empAddress;
	}
	
	public String getEmpEmailId() {
		return empEmailId;
	}
	
	public String getEmpPassword() {
		return empPassword;
	}
	
	public char getEmpGender() {
		return empGender;
	}
	
	public double getEmpSalary() {
		return empSalary;
	}
	
	@Override
	public String toString() {
		return "Employee [empId=" + empId + ", empName=" + empName + ", empAddress=" + empAddress + ", empEmailId="
				+ empEmailId + ", empPassword=" + empPassword + ", empGender=" + empGender + ", empSalary=" + empSalary
				+ "]";
	}

Step 2- Create Inner class: EmployeeBuilder

As above, we have made the constructor private. In order to create an object of the Employee class, we will create an inner class, EmployeeBuilder, and put all the member variables of the Employee class in the EmployeeBuilder class. This time, we make only setters for EmployeeBuilder, not getters, as we will get the member variables using the getters of the Employee class.

Employee.java

/**
 * Builder Design Pattern
 * 
 * @author paulsofts
 * */

public class Employee {
	
	private final int empId;
	private final String empName;
	private final String empAddress;
	private final String empEmailId;
	private final String empPassword;
	private final char empGender;
	private final double empSalary;
	
	
	/**
	 * 1. We create a private constructor. So, that anyone
	 *    can not create the object of class using constructor
	 *    initialization
	 * 
	 * 2. We are using EmployeeBuilder context to initialize
	 *    Employee class with the help of this(Current class, Employee
	 *    class context)
	 * @author paulsofts
	 * */
	
	private Employee() {
		
	}
	
	/**
	 * 1. We create only getter methods for Employee class
	 * 2. Not setter methods, as we do not want anyone to
	 * 	  set the data to employee object
	 * 
	 * @author paulsofts
	 * */
		
	public int getEmpId() {
		return empId;
	}
	
	public String getEmpName() {
		return empName;
	}
	
	public String getEmpAddress() {
		return empAddress;
	}
	
	public String getEmpEmailId() {
		return empEmailId;
	}
	
	public String getEmpPassword() {
		return empPassword;
	}
	
	public char getEmpGender() {
		return empGender;
	}
	
	public double getEmpSalary() {
		return empSalary;
	}
	
	@Override
	public String toString() {
		return "Employee [empId=" + empId + ", empName=" + empName + ", empAddress=" + empAddress + ", empEmailId="
				+ empEmailId + ", empPassword=" + empPassword + ", empGender=" + empGender + ", empSalary=" + empSalary
				+ "]";
	}

	/**
	 * 1. As the Constructor is private, We need to an
	 *    Inner class to create the object of Employee class
	 * 
	 * @author paulsofts
	 * */
	
	static class EmployeeBuilder {
		
		private int empId;
		private String empName;
		private String empAddress;
		private String empEmailId;
		private String empPassword;
		private char empGender;
		private double empSalary;
		
		public EmployeeBuilder() {
			
		}

		public void setEmpId(int empId) {
			this.empId = empId;
		}

		public void setEmpName(String empName) {
			this.empName = empName;
		}

		public void setEmpAddress(String empAddress) {
			this.empAddress = empAddress;
		}

		public void setEmpEmailId(String empEmailId) {
			this.empEmailId = empEmailId;
		}

		public void setEmpPassword(String empPassword) {
			this.empPassword = empPassword;
		}

		public void setEmpGender(char empGender) {
			this.empGender = empGender;
		}

		public void setEmpSalary(double empSalary) {
			this.empSalary = empSalary;
		}
		
	}

}

Step 3- Employee object initialization

Finally, we will initialize our Employee object using the inner class EmployeeBuilder. For this, we will change the return type of setter methods of the EmployeBuilder class from void to EmployeeBuilder type and return the current object of EmployeeBuilder (this).

Also, we create a build() method, in which we create an Employee object and pass this (the current class object, i.e., EmployeeBuilder) to its constructor. We will need to update our private constructor of the Employee class with the EmployeeBuilder type reference and use it to initialize the member variables of the Employee class.

Emplyee.java
package com.paulsofts.builderdesignpattern;

/**
 * Builder Design Pattern
 * 
 * @author paulsofts
 * */

public class Employee {
	
	private final int empId;
	private final String empName;
	private final String empAddress;
	private final String empEmailId;
	private final String empPassword;
	private final char empGender;
	private final double empSalary;
	
	
	/**
	 * 1. We create a private constructor. So, that anyone
	 *    can not create the object of class using constructor
	 *    initialization
	 * 
	 * 2. We are using EmployeeBuilder context to initialize
	 *    Employee class with the help of this(Current class, Employee
	 *    class context)
	 * @author paulsofts
	 * */
	
	private Employee(EmployeeBuilder builder) {
		
		this.empId = builder.empId;
		this.empName = builder.empName;
		this.empAddress = builder.empAddress;
		this.empEmailId = builder.empEmailId;
		this.empPassword = builder.empPassword;
		this.empGender = builder.empGender;
		this.empSalary = builder.empSalary;
		
	}
	
	/**
	 * 1. We create only getter methods for Employee class
	 * 2. Not setter methods, as we do not want anyone to
	 * 	  set the data to employee object
	 * 
	 * @author paulsofts
	 * */
		
	public int getEmpId() {
		return empId;
	}
	
	public String getEmpName() {
		return empName;
	}
	
	public String getEmpAddress() {
		return empAddress;
	}
	
	public String getEmpEmailId() {
		return empEmailId;
	}
	
	public String getEmpPassword() {
		return empPassword;
	}
	
	public char getEmpGender() {
		return empGender;
	}
	
	public double getEmpSalary() {
		return empSalary;
	}
	
	@Override
	public String toString() {
		return "Employee [empId=" + empId + ", empName=" + empName + ", empAddress=" + empAddress + ", empEmailId="
				+ empEmailId + ", empPassword=" + empPassword + ", empGender=" + empGender + ", empSalary=" + empSalary
				+ "]";
	}

	/**
	 * 1. As the Constructor is private, We need to an
	 *    Inner class to create the object of Employee class
	 * 
	 * @author paulsofts
	 * */
	
	static class EmployeeBuilder {
		
		private int empId;
		private String empName;
		private String empAddress;
		private String empEmailId;
		private String empPassword;
		private char empGender;
		private double empSalary;
		
		public EmployeeBuilder() {
			
		}
		
		/**
		 * 1. We will perform method chaining
		 *    while setting the fields we change the return type
		 *    to EmployeeBuilder, this we will get the same previously
		 *    created object 
		 * 
		 * @author paulsofts
		 * */
		
		public EmployeeBuilder setEmpId(int empId) {
			this.empId = empId;
			return this;
		}
		
		public EmployeeBuilder setEmpName(String empName) {
			this.empName = empName;
			return this;
		}
		
		public EmployeeBuilder setEmpAddress(String empAddress) {
			this.empAddress = empAddress;
			return this;
		}
		
		public EmployeeBuilder setEmpEmailId(String empEmailId) {
			this.empEmailId = empEmailId;
			return this;
		}
		
		public EmployeeBuilder setEmpPassword(String empPassword) {
			this.empPassword = empPassword;
			return this;
		}
		
		public EmployeeBuilder setEmpGender(char empGender) {
			this.empGender = empGender;
			return this;
		}
		
		public EmployeeBuilder setEmpSalary(double empSalary) {
			this.empSalary = empSalary;
			return this;
		}
		
		/**
		 * 1. We create an Employee object and pass the 
		 *    EmployeeBuilder context(this) to it.
		 * 
		 * @author paulsofts
		 * */
		
		public Employee build() {
			Employee employee = new Employee(this);
			return employee;
		}	
	}
}

Step 4- Create Main class

Now that we are done with our Employee class completely, we create a Main class to test the Builder design pattern.

Java
public class Main {
	
	public static void main(String[] args) {
		
		/**
		 * 1. We call the inner class with the help of outer class instance
		 * 2. Here, inner class is static so, we call it using the 
		 *    outer class name
		 * 3. After this, we will use method chaining to set the attributes
		 *    of our Employee class
		 * 
		 * @author paulsofts
		 * */
		Employee employee = new Employee.EmployeeBuilder()
				.setEmpId(1303579)
				.setEmpName("David")
				.setEmpAddress("Pune, Maharashtra")
				.setEmpEmailId("david@paulsofts.com")
				.setEmpPassword("david.123")
				.setEmpGender('M')
				.setEmpSalary(51168.00).build();
		
		System.out.println("Employee Information: " + employee.toString());
	}
}

As we run our main class, we will get the following output:

Output
Employee Information: Employee [empId=1303579, empName=David, empAddress=Pune, Maharashtra, empEmailId=david@paulsofts.com, empPassword=david.123, empGender=M, empSalary=51168.0]

Below, we have a screenshot of our output for the builder design pattern:

Builder design pattern
Fig 1- Output: Builder design pattern
Note: In our main class, while setting the member variables, we can even skip a few fields, but still our employee objects get created, which is not possible in the factory or abstract factory pattern without passing null values to the fields that we want to skip.

Leave a Reply

Your email address will not be published. Required fields are marked *