Builder Design Pattern

Introduction

  • The Builder Design Pattern is a creational design pattern that aids in the step-by-step construction of complex objects.

  • It is particularly useful when dealing with objects that have numerous optional fields.

Problem

When dealing with objects requiring a large number of parameters, constructors become unwieldy with either a big constructor or multiple constructors with all combinations of parameters.

Explanation

Analogy with House Construction

  • Think of an object, such as a house, which is a complex entity composed of various components like roof, window, walls, and door.

  • The builder design pattern allows the creation of the house step by step:

    Note - Builder Form is a mediatory form

    1. addRoof() ==> Builder Form

    2. addWindow() ==> Builder Form

    3. addWalls() ==> Builder Form

    4. addDoor() ==> Builder Form

    5. build() ==> Completes the object creation.

StringBuilder as an Example

  • StringBuilder in Java is an example of the Builder Design Pattern.

  • After every method (append(), insert(), etc.), it returns a StringBuilder in a mediatory form.

  • The actual object is obtained by calling toString().

Custom Builder Components

Every builder design comprises four crucial components:

  1. Target Object: This is the object we aim to create, and it should encompass numerous optional fields.

  2. Builder Object (Abstract): The builder object incorporates all the fields of the target object and includes the steps for creating or setting these fields. The builder object may take the form of an abstract class.

  3. Director: The director is responsible for possessing the logic to create the target object using the builder object.

  4. Client: The client utilizes the director to obtain the target object, ensuring a streamlined process of object creation.

Student Builder Example

Let's consider the creation of a student using the builder design pattern as an example. A student may possess both essential and optional fields in this context.

Student

package builder;
import java.util.List;

public class Student {
    int rollNumber;
    int age;
    String name;
    String fatherName;
    String motherName;
    List<String> subjects;

    public Student(StudentBuilder studentBuilder) {
        this.rollNumber = studentBuilder.rollNumber;
        this.age = studentBuilder.age;
        this.name = studentBuilder.name;
        this.fatherName = studentBuilder.fatherName;
        this.motherName = studentBuilder.motherName;
        this.subjects = studentBuilder.subjects;
    }

    @Override
    public String toString() {
        return "Student{" +
                "rollNumber=" + rollNumber +
                ", age=" + age +
                ", name='" + name + '\'' +
                ", fatherName='" + fatherName + '\'' +
                ", motherName='" + motherName + '\'' +
                ", subjects=" + subjects +
                '}';
    }
}

StudentBuilder

The StudentBuilder will serve as an abstract class containing all the fields of the Student Object. Each setter within this class will return the current object itself.

package builder;

import java.util.List;

public abstract class StudentBuilder {
    int rollNumber;
    int age;
    String name;
    String fatherName;
    String motherName;
    List<String> subjects;

    public StudentBuilder setRollNumber(int rollNumber) {
        this.rollNumber = rollNumber;
        return this;
    }

    public StudentBuilder setAge(int age) {
        this.age = age;
        return this;
    }

    public StudentBuilder setName(String name) {
        this.name = name;
        return this;
    }

    public StudentBuilder setFatherName(String fatherName) {
        this.fatherName = fatherName;
        return this;
    }

    public StudentBuilder setMotherName(String motherName) {
        this.motherName = motherName;
        return this;
    }

    public abstract StudentBuilder setSubjects();

    public Student build(){
        return new Student(this);
    }
}

EngineeringStudentBuilder

package builder;

import java.util.ArrayList;

public class EngineeringStudentBuilder extends StudentBuilder{
    @Override
    public StudentBuilder setSubjects() {
        this.subjects = new ArrayList<>();
        this.subjects.add("Mathematics");
        this.subjects.add("Python Programming");
        this.subjects.add("Data Structures and Algorithms");
        return this;
    }
}

MbaStudentBuilder

package builder;

import java.util.ArrayList;
import java.util.List;

public class MbaStudentBuilder extends StudentBuilder{

    @Override
    public StudentBuilder setSubjects() {
        this.subjects = new ArrayList<>();
        this.subjects.add("Micro Economics");
        this.subjects.add("Operation Management");
        this.subjects.add("Business studies");
        return this;
    }
}

Director

package builder;

public class Director {

    StudentBuilder studentBuilder;

    public Director(StudentBuilder studentBuilder) {
        this.studentBuilder = studentBuilder;
    }

    public Student createStudent(){
        if (studentBuilder instanceof MbaStudentBuilder){
            return this.studentBuilder.setAge(10).setFatherName("sarma").setMotherName("naga").setSubjects().build();
        } else {
            return this.studentBuilder.setAge(15).setFatherName("cool").setName("chetan").setSubjects().build();
        }
    }
}

Client

package builder;

public class Client {
    public static void main(String[] args) {
        Director director1 = new Director(new EngineeringStudentBuilder());
        Director director2 = new Director(new MbaStudentBuilder());
        Student engineeringStudent = director1.createStudent();n
        Student mbaStudent = director2.createStudent();

        System.out.println(engineeringStudent.toString());
        System.out.println(mbaStudent.toString());
        StringBuilder
    }
}

Builder Design vs Decorator Design Pattern

  • While Builder focuses on constructing a complex object step by step, the Decorator Design Pattern is a structural pattern that involves attaching new functionalities to an object dynamically.

  • The Decorator design pattern is a dynamic design approach, implying that given features f1, f2,... fn, each unique combination can generate a new object variety, analogous to pizzas. If we were to illustrate this with the builder design pattern, every combination would require a separate builder, resulting in 2^n builders. Conversely, with the Decorator pattern, there are only n distinct objects/decorators needed.

Conclusion

The Builder object will have the same set of fields as the target object, and at each step of the creation process, the builder will return itself as an intermediary object.