When
a class requires more arguments to instantiate then it is better to go for a
builder pattern. Let me explain with an example.
For
example, you are developing an application to maintain all the employees’
information of organization ‘ABC’. Initially, I designed Employee model class
like below.
Approach 1: Design Employee model
class by providing setter and getter methods.
Employee.java
public class Employee { private int id; private String firstName; private String lastName; private float salary; private String designation; public int getId() { return id; } public void setId(int id) { this.id = id; } 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 float getSalary() { return salary; } public void setSalary(float salary) { this.salary = salary; } public String getDesignation() { return designation; } public void setDesignation(String designation) { this.designation = designation; } }
There
is a problem with this approach. Since Employee class provides setter methods,
you can’t make the Employee class immutable.
Approach 2: We can make the class
‘Employee’ immutable, by not providing setter methods and providing different
combinations of constructors to initialize instance properties.
Employee.java
public class Employee { private int id; private String firstName; private String lastName; private float salary; private String designation; private static final String DEFAULT_NAME = "no_name"; private static final int DEFAULT_ID = -1; private static final String DEFAULT_DESIGNATION = "Engineer"; private static final float DEFAULT_SALARY = 25000; public Employee() { this(DEFAULT_ID, DEFAULT_NAME, DEFAULT_NAME, DEFAULT_SALARY, DEFAULT_DESIGNATION); } public Employee(int id) { this(id, DEFAULT_NAME, DEFAULT_NAME, DEFAULT_SALARY, DEFAULT_DESIGNATION); } public Employee(int id, String firstName) { this(id, firstName, DEFAULT_NAME, DEFAULT_SALARY, DEFAULT_DESIGNATION); } public Employee(int id, String firstName, String lastName) { this(id, firstName, lastName, DEFAULT_SALARY, DEFAULT_DESIGNATION); } public Employee(int id, String firstName, String lastName, float salary, String designation) { this.id = id; this.firstName = firstName; this.lastName = lastName; this.salary = salary; this.designation = designation; } public int getId() { return id; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public float getSalary() { return salary; } public String getDesignation() { return designation; } }
There
is a problem with above model class, As you see the definition of Employee
class, I provided 4 different constructors to initialize different combination
of properties. What if my class has more instance properties, I will end up in
more and more constructor definitions, which is not feasible. We can solve both
the problems by providing builder object.
Approach 3: Define Employee model
class using builder pattern.
Employee.java
public class Employee { private int id; private String firstName; private String lastName; private float salary; private String designation; private Employee() { } public int getId() { return id; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public float getSalary() { return salary; } public String getDesignation() { return designation; } public static class EmployeeBuilder { private static final String DEFAULT_NAME = "no_name"; private static final int DEFAULT_ID = -1; private static final String DEFAULT_DESIGNATION = "Engineer"; private static final float DEFAULT_SALARY = 25000; private int id; private String firstName; private String lastName; private float salary; private String designation; public EmployeeBuilder() { this.id = DEFAULT_ID; this.firstName = this.lastName = DEFAULT_NAME; this.salary = DEFAULT_SALARY; this.designation = DEFAULT_DESIGNATION; } public EmployeeBuilder id(int id) { this.id = id; return this; } public EmployeeBuilder firstName(String firstName) { this.firstName = firstName; return this; } public EmployeeBuilder lastName(String lastName) { this.lastName = lastName; return this; } public EmployeeBuilder salary(float salary) { this.salary = salary; return this; } public EmployeeBuilder designation(String designation) { this.designation = designation; return this; } public Employee build() { Employee emp = new Employee(); emp.designation = this.designation; emp.firstName = this.firstName; emp.id = this.id; emp.lastName = lastName; emp.salary = salary; return emp; } } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Employee [id=").append(id).append(", firstName=").append(firstName).append(", lastName=") .append(lastName).append(", salary=").append(salary).append(", designation=").append(designation) .append("]"); return builder.toString(); } }
Test.java
public class Test { public static void main(String args[]) { Employee.EmployeeBuilder builder = new Employee.EmployeeBuilder(); Employee emp = builder.id(553) .firstName("Hari krishna") .lastName("Gurram") .designation("Software Engineer") .build(); System.out.println(emp); } }
Output
Employee [id=553, firstName=Hari krishna, lastName=Gurram, salary=25000.0, designation=Software Engineer]
In
real world applications, it is always good coding practice to code for an
interface. For example, you can define a Builder interface like below, and make
the EmployeeBuilder class implements the Builder interface.
public interface Builder<T> { public T build(); }
Implement
EmployeeBuilder like below.
public static class EmployeeBuilder implements Builder<Employee> { private static final String DEFAULT_NAME = "no_name"; private static final int DEFAULT_ID = -1; private static final String DEFAULT_DESIGNATION = "Engineer"; private static final float DEFAULT_SALARY = 25000; private int id; private String firstName; private String lastName; private float salary; private String designation; public EmployeeBuilder() { this.id = DEFAULT_ID; this.firstName = this.lastName = DEFAULT_NAME; this.salary = DEFAULT_SALARY; this.designation = DEFAULT_DESIGNATION; } public EmployeeBuilder id(int id) { this.id = id; return this; } public EmployeeBuilder firstName(String firstName) { this.firstName = firstName; return this; } public EmployeeBuilder lastName(String lastName) { this.lastName = lastName; return this; } public EmployeeBuilder salary(float salary) { this.salary = salary; return this; } public EmployeeBuilder designation(String designation) { this.designation = designation; return this; } @Override public Employee build() { Employee emp = new Employee(); emp.designation = this.designation; emp.firstName = this.firstName; emp.id = this.id; emp.lastName = lastName; emp.salary = salary; return emp; } }
You may like
No comments:
Post a Comment