Builder Design Pattern

The Builder design pattern provides a simple way to construct complex objects using a step by step approach. A Builder creates parts of the complex object every time it is called and maintains the intermediate state of complex object. When the object creation finishes, client retrieves the object from the Builder.

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

The Builder Design Pattern involves the following key components:
  • 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.
Employee.java
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

Important Points About Builder Pattern
  • 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.

Best Practices of Builder 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.

Related Topics
Prototype Design Pattern
Observer Design Pattern
Flyweight Design Pattern
Command Design Pattern
Strategy Design Pattern
List of Design Patterns