Builder Pattern
Let us understand the Builder Pattern
Builder Pattern is mainly used to construct complex objects, which means objects having lot of attributes (some of them are optional).
And it defines same object building process (as the name indicates) to construct any number of complex objects.
In this pattern, construction of complex object is separated from its representation.
Problem with Factory and Abstract factory patterns
Builder pattern was introduced to address the problem with factory and abstract factory patterns when object contains lot of attributes
If we consider Factory or Abstract factory pattern when object contains lot of attributes then we will face below problems
1.We will end up with sending too many arguments to factory class which can become error prone
2.Some parameters are optional sometimes and in factory pattern, it’s required
to send all parameters and we have to send NULL values to all optional attributes.
3.Since we are passing many parameters to factory class, object creation process is very complex within factory class.
Problem with constructors and setters
We can use constructors with all the required attributes and then define setter method to set the optional attributes.
When we use this approach, the problem is object state will be inconsistent until all the setters are called to set the attributes explicitly.
In order to create complete object as per the requirement without any of the above problems, we use Builder pattern
Builder pattern solves the above problem by defining a way to build the object step by step and provide a single method that will actually return the final required object.
When to use Builder pattern?
Prefer Builder pattern when we need to create object which has lot of attributes in which some of them are optional and some of them are required attributes.
Let’s understand and implement Builder pattern in practical
Requirement
Create Employee object with attributes FirstName,lastName,age,mobile,address,salary
Case 1:
Assume all attributes are required attributes
In this case, we don’t need to use Builder pattern, we can just define Employee class with one constructor which takes all attributes as parameters
We just need to create object by passing all attributes as all are required.
Below code is sufficient enough to address above case
- package com.kb.builder;
- public class Employee {
- private String firstName;//required
- private String lastName;//required
- private int age;//required
- private long mobile;//required
- private String address;//optional
- private double salary;//optional
- public Employee(String firstName, String lastName, int age, long mobile, String address, double salary) {
- this.firstName = firstName;
- this.lastName = lastName;
- this.age = age;
- this.mobile = mobile;
- this.address = address;
- this.salary = salary;
- }
- }
package com.kb.builder; public class Employee { private String firstName;//required private String lastName;//required private int age;//required private long mobile;//required private String address;//optional private double salary;//optional public Employee(String firstName, String lastName, int age, long mobile, String address, double salary) { this.firstName = firstName; this.lastName = lastName; this.age = age; this.mobile = mobile; this.address = address; this.salary = salary; } }
Case 2:
Assume some attributes like address, salary attributes are optional
In this case, if we don’t use Builder pattern, then we have below 2 options
option 1:
Define multiple constructors
One which takes only required attributes as parameters
One which takes all the required attributes plus the first optional attribute
One which takes all the required attributes plus first and second optional attributes and so on
It looks something like below code
- package com.kb.builder;
- public class Employee {
- private String firstName;
- private String lastName;
- private int age;
- private long mobile;
- private String address;
- private double salary;
- public Employee(String firstName, String lastName, int age, long mobile) {
- this.firstName = firstName;
- this.lastName = lastName;
- this.age = age;
- this.mobile = mobile;
- }
- public Employee(String firstName, String lastName, int age, long mobile, String address) {
- this.firstName = firstName;
- this.lastName = lastName;
- this.age = age;
- this.mobile = mobile;
- this.address = address;
- }
- public Employee(String firstName, String lastName, int age, long mobile, String address, double salary) {
- this.firstName = firstName;
- this.lastName = lastName;
- this.age = age;
- this.mobile = mobile;
- this.address = address;
- this.salary = salary;
- }
- }
package com.kb.builder; public class Employee { private String firstName; private String lastName; private int age; private long mobile; private String address; private double salary; public Employee(String firstName, String lastName, int age, long mobile) { this.firstName = firstName; this.lastName = lastName; this.age = age; this.mobile = mobile; } public Employee(String firstName, String lastName, int age, long mobile, String address) { this.firstName = firstName; this.lastName = lastName; this.age = age; this.mobile = mobile; this.address = address; } public Employee(String firstName, String lastName, int age, long mobile, String address, double salary) { this.firstName = firstName; this.lastName = lastName; this.age = age; this.mobile = mobile; this.address = address; this.salary = salary; } }
The above code definitely works but it creates more problem when number of attributes increases in a class.
Number of constructors also increases and leads to lot of confusion and it becomes very difficult to maintain the code
option 2:
Create class with all the attributes and getters and setters for each attribute
- package com.kb.builder;
- public class Employee {
- private String firstName;
- private String lastName;
- private int age;
- private long mobile;
- private String address;
- private double salary;
- public String getFirstName() {
- return firstName;
- }
- public void setFirstName(String firstName) {
- this.firstName = firstName;
- }
- public String getLastName() {
- return lastName;
- }
- public void setLastName(String lastName) {
- this.lastName = lastName;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- public long getMobile() {
- return mobile;
- }
- public void setMobile(long mobile) {
- this.mobile = mobile;
- }
- public String getAddress() {
- return address;
- }
- public void setAddress(String address) {
- this.address = address;
- }
- public double getSalary() {
- return salary;
- }
- public void setSalary(double salary) {
- this.salary = salary;
- }
- }
package com.kb.builder; public class Employee { private String firstName; private String lastName; private int age; private long mobile; private String address; private double salary; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public long getMobile() { return mobile; } public void setMobile(long mobile) { this.mobile = mobile; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } }
In this case, we can call setter for whatever the attribute we need to set value.
But the problem here is, Object will be inconsistent until all the setters are invoked for required attributes.
The most feasible solution to solve above problem is Builder pattern
Steps to implement the Builder pattern
1) Create a EmployeeBuilder static nested class and then copy all the attributes from the outer Employee class to the nested EmployeeBuilder class.
2) Builder class should have a public constructor with all the required attributes as parameters.
3) Builder class should have methods to set the optional parameters and it should return the same
Builder object after setting the optional attribute.
4) The final step is to provide a build() method in the builder class that will return the Object needed by client program. For this we need to have a private constructor in the Employee Class with EmployeeBuilder class as argument
- package com.kb.builder;
- public class Employee {
- private String firstName;//required
- private String lastName;//required
- private int age;//required
- private long mobile;//required
- private String address;//optional
- private double salary;//optional
- private Employee(EmployeeBuilder builder) {
- this.firstName = builder.firstName;
- this.lastName = builder.lastName;
- this.age = builder.age;
- this.mobile = builder.mobile;
- this.address = builder.address;
- this.salary = builder.salary;
- }
- public String getFirstName() {
- return firstName;
- }
- public String getLastName() {
- return lastName;
- }
- public int getAge() {
- return age;
- }
- public long getMobile() {
- return mobile;
- }
- public String getAddress() {
- return address;
- }
- public double getSalary() {
- return salary;
- }
- public static class EmployeeBuilder {
- private String firstName;
- private String lastName;
- private int age;
- private long mobile;
- private String address;
- private double salary;
- public EmployeeBuilder(String firstName, String lastName, int age, long mobile) {
- this.firstName = firstName;
- this.lastName = lastName;
- this.age = age;
- this.mobile = mobile;
- }
- public EmployeeBuilder address(String address) {
- this.address = address;
- return this;
- }
- public EmployeeBuilder salary(double salary) {
- this.salary = salary;
- return this;
- }
- public Employee build() {
- return new Employee(this);
- }
- }
- }
package com.kb.builder; public class Employee { private String firstName;//required private String lastName;//required private int age;//required private long mobile;//required private String address;//optional private double salary;//optional private Employee(EmployeeBuilder builder) { this.firstName = builder.firstName; this.lastName = builder.lastName; this.age = builder.age; this.mobile = builder.mobile; this.address = builder.address; this.salary = builder.salary; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public int getAge() { return age; } public long getMobile() { return mobile; } public String getAddress() { return address; } public double getSalary() { return salary; } public static class EmployeeBuilder { private String firstName; private String lastName; private int age; private long mobile; private String address; private double salary; public EmployeeBuilder(String firstName, String lastName, int age, long mobile) { this.firstName = firstName; this.lastName = lastName; this.age = age; this.mobile = mobile; } public EmployeeBuilder address(String address) { this.address = address; return this; } public EmployeeBuilder salary(double salary) { this.salary = salary; return this; } public Employee build() { return new Employee(this); } } }
Now client can use this builder pattern as below
- package com.kb.builder;
- public class BuilderClient {
- public static void main(String[] args) {
- //case 1: Create Employee with all required and one optional attribute
- Employee e1 = new Employee.EmployeeBuilder("John", "Dian",28,9999888812l)
- .address("test address")
- .build();
- //case 2: Create Employee with all required and both optional attributes
- Employee e2 = new Employee.EmployeeBuilder("John", "Dian",28,9999888812l)
- .address("test address")
- .salary(40000)
- .build();
- }
- }
package com.kb.builder; public class BuilderClient { public static void main(String[] args) { //case 1: Create Employee with all required and one optional attribute Employee e1 = new Employee.EmployeeBuilder("John", "Dian",28,9999888812l) .address("test address") .build(); //case 2: Create Employee with all required and both optional attributes Employee e2 = new Employee.EmployeeBuilder("John", "Dian",28,9999888812l) .address("test address") .salary(40000) .build(); } }
We can see how easy
it is to construct the object using Builder pattern
We can create object with required attributes and optional attributes as per the requirement by using build method appropriately.
Advantages of Builder pattern
1) It minimizes the number of parameters passed to constructor and also there is no need to pass null for optional parameters to the constructor
2) This pattern always instantiate the complete object rather than creating an object with incomplete state until the appropriate “setter” methods are called explicitly to set additional fields.
3) Client code is more readable and easy to create object
Disadvantages of Builder pattern
The number of lines in the code will be more as we need to construct the entire Builder object