Builder Design Pattern

What is a builder design pattern?

Builder design pattern allows us to construct complex objects step by step. It produces different types and representations of an object using the same construction process.

Builder is a creational design pattern

Advantages of builder design pattern:

  • Reduces the code used to initialize the object
  • The number of parameters in the constructor reduces and makes it very readable.
  • It separates the construction and representation of an object.
  • It provides better control over the object construction process.
  • Immutable objects can be built without much complex logic in the object-building process.
  • It supports changing the internal representation of objects.

Disadvantages of builder design pattern:

  • It requires writing extra code to implement the builder, more code requires more maintenance.
  • Requires creating a separate ConcreteBuilder for different types of objects.

Example of builder design pattern in TypeScript:

// Client.ts
import {UserBuilder} from "./UserBuilder";
import {User} from "./User";

const userObj: User = new UserBuilder('Joe Smith')
    .setAge(24)
    .setPhone("01911016")
    .setAddress("Munich, Germany")
    .build();
console.log(userObj.getName() + " " + userObj.getAge() + " " + userObj.getPhone() + " " + userObj.getAddress());


// UserBuilder.ts
import { User } from "./User";

export class UserBuilder {
    private name: string;
    private age: number;
    private phone: string;
    private address: string;

    constructor(name: string) {
        this.name = name;
    }

    getName() {
        return this.name;
    }
    setAge(value: number): UserBuilder {
        this.age = value;
        return this;
    }
    getAge() {
        return this.age;
    }
    setPhone(value: string): UserBuilder {
        this.phone = value;
        return this;
    }
    getPhone() {
        return this.phone;
    }
    setAddress(value: string): UserBuilder {
        this.address = value;
        return this;
    }
    getAddress() {
        return this.address;
    }

    build(): User {
        return new User(this);
    }
}


// User.ts
import { UserBuilder } from "./UserBuilder";

export class User {
    private name: string;
    private age: number;
    private phone: string;
    private address: string;

    constructor(builder: UserBuilder) {
        this.name = builder.getName();
        this.age = builder.getAge();
        this.phone = builder.getPhone();
        this.address = builder.getAddress()
    }

    getName() {
        return this.name;
    }
    getAge() {
        return this.age;
    }
    getPhone() {
        return this.phone;
    }
    getAddress() {
        return this.address;
    }
}


Example of builder design pattern in Java:

// Product class: The object that will be built
class House {
    // Required parameters
    private String foundation;
    private String structure;
    
    // Optional parameters
    private boolean hasGarage;
    private boolean hasSwimmingPool;
    private boolean hasGarden;

    // Private constructor to prevent direct instantiation
    private House(HouseBuilder builder) {
        this.foundation = builder.foundation;
        this.structure = builder.structure;
        this.hasGarage = builder.hasGarage;
        this.hasSwimmingPool = builder.hasSwimmingPool;
        this.hasGarden = builder.hasGarden;
    }

    // Getters for accessing properties
    public String getFoundation() {
        return foundation;
    }

    public String getStructure() {
        return structure;
    }

    public boolean hasGarage() {
        return hasGarage;
    }

    public boolean hasSwimmingPool() {
        return hasSwimmingPool;
    }

    public boolean hasGarden() {
        return hasGarden;
    }

    @Override
    public String toString() {
        return "House [Foundation=" + foundation + ", Structure=" + structure 
                + ", Garage=" + hasGarage + ", Swimming Pool=" + hasSwimmingPool 
                + ", Garden=" + hasGarden + "]";
    }

    // Static nested builder class
    public static class HouseBuilder {
        // Required parameters
        private String foundation;
        private String structure;

        // Optional parameters
        private boolean hasGarage;
        private boolean hasSwimmingPool;
        private boolean hasGarden;

        // Constructor with required parameters
        public HouseBuilder(String foundation, String structure) {
            this.foundation = foundation;
            this.structure = structure;
        }

        // Setter methods for optional parameters
        public HouseBuilder setGarage(boolean hasGarage) {
            this.hasGarage = hasGarage;
            return this; // Return the builder to chain method calls
        }

        public HouseBuilder setSwimmingPool(boolean hasSwimmingPool) {
            this.hasSwimmingPool = hasSwimmingPool;
            return this;
        }

        public HouseBuilder setGarden(boolean hasGarden) {
            this.hasGarden = hasGarden;
            return this;
        }

        // Build method to construct the final House object
        public House build() {
            return new House(this);
        }
    }
}

// Client code to demonstrate the Builder pattern
public class BuilderPatternExample {
    public static void main(String[] args) {
        // Creating a house using the builder
        House house = new House.HouseBuilder("Concrete", "Wood")
                                .setGarage(true)
                                .setSwimmingPool(false)
                                .setGarden(true)
                                .build();

        System.out.println(house);
    }
}