The Builder design pattern provides a simple way to construct complex objects using a step by step approach.
A builder class separates the construction of a complex object from its representation so that the same construction process can create different representations. This pattern is especially useful when an object needs to be created with a large number of optional components or configuration parameters.
Structure of the Builder Design Pattern
- Product : The class for which the construction is separated. It represents the complex object to be created.
- Builder :Either an interface or an abstract class outlining the sequential steps for fabricating the product. It typically incorporates methods for configuring diverse aspects of the product.
- ConcreteBuilder :A class implementing the Builder interface, providing detailed implementations for the construction phases. It is tasked with assembling and delivering the final product.
- Director : An optional class that orchestrates the assembly procedure by utilizing a builder object. It guides the construction of the product in a stepwise manner.
- Client : The class that interacts with the builder to construct the product. It can leverage the director or engage directly with a builder to tailor the construction procedure.
Advantages of Builder Pattern
- Flexible Object Creation : The Builder Pattern enables a versatile and gradual assembly of intricate entities. Users can tailor the assembly procedure to meet their specific needs.
- Variations of the Same Product : In scenarios involving the construction of multiple product variations, the Builder pattern allows the development of distinct builders, each designed for a particular portrayal of the product. This encourages the reuse of code and simplifies upkeep.
- Default Values and Optional Components : The builder pattern facilitates the specification of default settings for elements, permitting users to define only the elements they require. This adaptability is particularly valuable when dealing with optional or configurable aspects of an object.
- Simplified Client Implementation : Clients using the Builder pattern can construct complex objects without the need to understand the intricate details of the object's construction process. This simplifies client implementation and enhances maintainability.
- Consistent Product State : The builder pattern contributes to ensuring that the assembled objects maintains a consistent state. By managing validations and enforcing mandatory components during assembly, it minimizes the likelihood of creating inconsistent objects.
- Testability : The separation of concerns in the Builder pattern makes it easier to test the construction process and the resulting object. Clients can inject mock builders or substitute components during testing, facilitating unit testing.
- Separation of Concerns : The segregation of the object construction procedure from the object's representation advocates for a clear division of responsibilities. The builder class is tasked with constructing the object, while the product class embodies the ultimate outcome.
When we should use Builder Pattern
- When we needs different representations for the objects that are being built. Builder class provides a way to configure an object before creating it.
- The algorithm of creating a complex object is independent from the parts that actually compose the complex object.
- Once an object is fully created, you don't want to change it’s state.
Implementation of Builder Design Pattern
- We will create an Employee class containing employee information and a static nested class called EmployeeBuilder inside the Employee class whose object will be build Employee objects.
- Employee class must provide only public getter functions not setter function to ensure that employee object is immutable form outside.
- EmployeeBuilder class will have same set of fields as Employee class.
- EmployeeBuilder class will expose public method for setting all optional fields of EmployeeBuilder class. All setter functions will return a reference to current builder object.
- build() method of EmployeeBuilder class will create a new instance of Employee object by passing it self as constructor parameter. Constructor will copy the fields from builder object to Employee object. Clients will call build() method once they finished setting fields of builder.
public class Employee { private final String name; // Required private final String department; // Optional private final int age; // Optional private final int salary ; // Optional private final String rank; // Optional private Employee(EmployeeBuilder builder) { this.name = builder.name; this.department = builder.department; this.age = builder.age; this.salary = builder.salary; this.rank = builder.rank; } // we will only provide public getters to ensure that // object is immutable public String getName() { return this.name; } public String getDepartment() { return this.department; } public int getAge() { return this.age; } public int getSalary() { return this.salary; } public String getRank() { return this.rank; } public static class EmployeeBuilder { private final String name; private String department; private int age; private int salary; private String rank; public EmployeeBuilder(String name) { this.name = name; } public EmployeeBuilder setDepartment(String department) { this.department = department; return this; } public EmployeeBuilder setAge(int age) { this.age = age; return this; } public EmployeeBuilder setSalary(int salary) { this.salary = salary; return this; } public EmployeeBuilder setRank(String rank) { this.rank = rank; return this; } //Return the constructed Employee object to client public Employee build() { return new Employee(this); } } @Override public String toString(){ return "Name : " + this.name + "\nDepartment : " + this.department + "\nAge : " + this.age + "\nSalary : " + this.salary + "\nRank : " + this.rank; } }BuilderPatternDemo.java
public class BuilderPatternDemo { public static void main(String[] args) { // creating Employee object by setting all fields Employee emp1 = new Employee.EmployeeBuilder("George") .setDepartment("Finance").setAge(35).setSalary(30000) .setRank("2").build(); System.out.println(emp1.toString() + "\n"); // creating Employee object by setting only 3 fields Employee emp2 = new Employee.EmployeeBuilder("Mark") .setDepartment("Finance").setAge(35).build(); System.out.println(emp2.toString() + "\n"); // creating Employee object by setting only 1 field Employee emp3 = new Employee.EmployeeBuilder("Andy").build(); System.out.println(emp3.toString() + "\n"); } }
Output
Name : George Department : Finance Age : 35 Salary : 30000 Rank : 2 Name : Mark Department : Finance Age : 35 Salary : 0 Rank : null Name : Andy Department : null Age : 0 Salary : 0 Rank : null
- It encapsulates the construction of complex object and allow it to be constructed step by step.
- Builder pattern often builds a Composite.
- It is a creational design pattern.
- Design the builder interface to allow stepwise construction of the object. Each method in the builder should correspond to setting a specific component or property of the object.
- Consider making the product class immutable, especially if it represents an entity with a fixed state. Immutability ensures that the constructed object remains unchanged after creation.
- Keep the construction process separate from the representation of the object. The builder is responsible for constructing the object, while the product class represents the final result.
- Handle optional components or properties gracefully in the builder. Allow clients to set only the components they need, and provide default values or omit certain steps if they are not specified.
- Implement a fluent interface in the builder, allowing method chaining. This enhances readability and makes the construction process more intuitive.
- Use clear and descriptive names for methods in the builder interface to convey their purpose. This improves the readability of the code and makes it easier for clients to understand how to use the builder.
- Ensure that the product is in a consistent state when the construction is complete. The builder should handle any necessary validations to prevent the creation of invalid objects.
- If your product class has a large number of optional components, avoid creating a telescoping constructor with numerous parameters. The builder pattern provides a more scalable and readable solution.
Prototype Design Pattern |
Observer Design Pattern |
Flyweight Design Pattern |
Command Design Pattern |
Strategy Design Pattern |
List of Design Patterns |