DEV Community

Vivek Alhat
Vivek Alhat

Posted on

3

The Builder Design Pattern

When creating objects in programming, sometimes we need to set many optional values. If we use constructors for this, it can get messy quickly and cause constructor hell. The builder pattern helps us solve this problem by providing a step-by-step way to create objects.

Why Use the Builder Pattern?

Consider a User class where some properties (like age and email) are optional. Without the Builder Pattern, you might need multiple constructors or pass undefined for missing values. This can make code hard to read.

The Builder Pattern solves this by:

  • Making object creation more readable.
  • Avoiding unnecessary or confusing constructor arguments.

Example: Creating a User Object with a Builder

Let's take a look at how the Builder Pattern works in TypeScript.

Step 1: Define the User Class

class User {
  constructor(
    public name: string,
    public age?: number,
    public email?: string
  ) {}
}
Enter fullscreen mode Exit fullscreen mode

This User class has three properties: name, age, and email. Only name is required.

Step 2: Create a UserBuilder Class

interface IUserBuilder {
  setName(name: string): this;
  setAge(age: number): this;
  setEmail(email: string): this;
  build(): User;
}

class UserBuilder implements IUserBuilder {
  private user: Partial<User> = {};

  setName(name: string): this {
    this.user.name = name;
    return this;
  }

  setAge(age: number): this {
    this.user.age = age;
    return this;
  }

  setEmail(email: string): this {
    this.user.email = email;
    return this;
  }

  build(): User {
    if (!this.user.name) {
      throw new Error("User must have a name");
    }
    return new User(this.user.name, this.user.age, this.user.email);
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Using the UserBuilder

const user = new UserBuilder()
  .setName("John Doe")
  .setAge(35)
  .setEmail("johndoe@email.com")
  .build();

console.log(user);
Enter fullscreen mode Exit fullscreen mode

Output:

User { name: 'John Doe', age: 35, email: 'johndoe@email.com' }
Enter fullscreen mode Exit fullscreen mode

If we try to create a user without a name, we get an error:

const newUser = new UserBuilder().setAge(70).build(); // Error: User must have a name
Enter fullscreen mode Exit fullscreen mode

Benefits of Using the Builder Pattern

Improves readability – Each property is set explicitly.
Reduces constructor overloads – No need to pass undefined for optional parameters.
Flexible object creation – Easily add or remove properties without modifying the constructor.

When to Use the Builder Pattern

Use the Builder Pattern when:

  • You have a class with many optional properties.
  • You want to make object creation more readable.
  • You want to enforce required fields before creating the object.

Conclusion

The Builder Pattern is a simple yet powerful way to create objects, especially when dealing with optional parameters. It makes your code cleaner, easier to understand, and less error-prone.

I am implementing design patterns in Go, Python, and TypeScript. You can find the repository here.

Happy coding! 🚀

Top comments (0)