Builder Pattern in Java

HarikaHarika
4 min read

Builder Pattern is a creational pattern, that enables you to create a complex object step by step in a easier way. This is mainly used when an object is complex, like if it has too many attributes. It can produce different representations with same construction code. The Builder design pattern eliminates the need for multiple overloaded constructors by providing a flexible way to create objects with optional parameters.

When to use ?

  • When you have a constructor with several optional parameters.

  • When you need to construct complex objects with different representations or configurations.

  • When you want a common interface for constructing different representations of an object.

Components of Builder Patterns :

Product : Product refers complex object that has to be created. Usually it has many optional parameters.

Builder : Builder refers to an interface (or an abstract class), which has all the methods used to create the object(product).

Concrete Builder : These concrete Builder classes implement the Builder interfaces and provide implementations. Each concrete Builder can produce different variation of the product.

Director : Constructs an object using the Builder interface. It defines the order in which to call the builder steps.

Client : Creates the builder, passes it to the Director, triggers the object construction and uses the final product.

For Example

Imagine that you want to design a software that collects users' details and stores in a database. And we know a user profile can have optional fields as well. So, you will create a User class with the all the necessary fields. We create a constructor that accepts the all the fields. Since there could be some attributes, which are optional, we may need to pass null values to those fields. Say we have fields such as firstName, middleName, lastName, phoneNumber, address, age, secondaryPhoneNumber.

Without Builder Patterns, whenever we want to create a user profile, we create a constructor with all the fields and, and pass null values for optional fields right ?

public class UserProfile {
    private final String firstName;
    private final String middleName;
    private final String lastName;
    private final String phoneNumber;
    private final String address;
    private final int age;
    private final String secondaryPhoneNumber;

    // Constructor 1: All parameters
    public UserProfile(String firstName, String middleName, String lastName, String phoneNumber, 
                      String address, int age, String secondaryPhoneNumber){
        this.firstName = firstName;
        this.middleName = middleName;
        this.lastName = lastName;
        this.phoneNumber = phoneNumber;
        this.address = address;
        this.age = age;
        this.secondaryPhoneNumber = secondaryPhoneNumber;
    }

    // Constructor 2: Only required parameters
    public UserProfile(String firstName, String phoneNumber) {
        this(firstName,"", "", phonenumber, "", "", "", "");
    }

    // Constructor 3: Some common parameters
    public UserProfile(String firstName, String lastName, int age) {
        this(firstName, "",lastName, "", "", 0, "");
    }

    // Constructor 4: Another combination
    public UserProfile(String firstName, String middleName, String lastName, 
                      String phoneNumber, String address) {
        this(firstName, middleName firstName, lastName, phoneNumber, phoneNumber,
              address, 0);
    }

    // Getters
    public String getFirstName() { return firstName; }
    public String getMiddleName() { return middleName; }
    public String getLastName() { return lastName; }
    public String getPhoneNumber() { return phoneNumber; }
    public String getAddress() { return address; }
    public int getAge() { return age; }
    public String secondaryPhoneNumber() { return secondaryPhoneNumber; }
}

The builder design pattern lets us create an object step by step without passing all the values to the constructor.

// Product Class
public class UserProfile {
    // Required fields
    private final String firstName;
    private final String middleName;
    private final String lastName;
    private final String phoneNumber;
    private final String address;
    private final int age;
    private final boolean secondaryPhoneNumber;

    private UserProfile(UserProfileBuilder builder) {
        this.firstName = builder.firstName;
        this.middleName = builder.middleName;
        this.lastName = builder.lastName;
        this.phoneNumber = builder.phoneNumber;
        this.address = builder.address;
        this.age = builder.age;
        this.secondaryPhoneNumber = builder.secondaryPhoneNumber;
    }

    // Builder Class
    public static class UserProfileBuilder {
        private String firstName;
        private String middleName;
        private String lastName;
        private String phoneNumber;
        private String address;
        private int age;
        private String secondaryPhoneNumber;

        public UserProfileBuilder(String firstName, String phoneNumber) {
            this.userId = userId;
            this.email = email;
        }

       public UserProfileBuilder setMiddleName(String middkeName) {
            this.middleName = middleName;
            return this;
        }

        public UserProfileBuilder setLastName(String lastName) {
            this.lastName = lastName;
            return this;
        }

        public UserProfileBuilder setAddress(String address) {
            this.address = address;
            return this;
        }

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

        public UserProfileBuilder setsecondaryPhoneNumber(String secondaryPhoneNumber) {
            this.secondaryPhoneNumber = secondaryPhoneNumber;
            return this;
        }

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

    // Getters
    public String getFirstName() { return firstName; }
    public String getmiddleName() { return middleName; }
    public String getLastName() { return lastName; }
    public String getPhoneNumber() { return phoneNumber; }
    public String getAddress() { return address; }
    public int getAge() { return age; }
    public String secondaryPhoneNumber() { return secondaryPhoneNumber; }
}

And users can create an object as they wish with different number of parameters.

Example is given below:

public class BuilderPatternDemo {
    public static void main(String[] args) {
        UserProfile user = new UserProfile.UserProfileBuilder("john", "9090909090")
                .setMiddleName("marc")
                .setLastName("JT")
                .setAge(30)
                .setPhoneNumber("+1-234-567-8900")
                .setAddress("123 Street, City")
                .setsecondaryPhoneNumber("9898989898")
                .build();

        // Minimal user profile with only required fields
        UserProfile minimalUser = new UserProfile.UserProfileBuilder("Jai", "7675787922")
                .build();
    }
}

By using Builder Pattern, we can achieve,

  • clear separation between construction and representation

  • fine control over construction process

  • objects are immutable once constructed

  • handle optional parameters

  • more readable than telescoping constructors

  • Validates data before object creation

0
Subscribe to my newsletter

Read articles from Harika directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Harika
Harika