← Back to the blog

Patterns · video

Builder — design patterns

Builder, a creational design pattern for objects that carry many optional fields.

Video tutorial for this article · Open on YouTube ↗

Builder is a creational design pattern: it helps you construct objects. It earns its place when a constructor has so many arguments that you are no longer sure you are programming rather than writing a novel. When a class has many fields, especially optional ones, Builder makes the calling code easier to read.

Start with a plain User class and its constructor.

class User {
  private username: string;
  private email: string;
  private phoneNumber?: string;
  private avatar?: string;

  constructor(username: string, email: string, phoneNumber?: string, avatar?: string) {
    this.username = username;
    this.email = email;
    this.phoneNumber = phoneNumber;
    this.avatar = avatar;
  }
}

This is how new objects get created today.

class User {
  private username: string;
  private email: string;
  private phoneNumber?: string;
  private avatar?: string;

  constructor(username: string, email: string, phoneNumber?: string, avatar?: string) {
    this.username = username;
    this.email = email;
    this.phoneNumber = phoneNumber;
    this.avatar = avatar;
  }
}

You might say this still looks normal. Picture a class with 10 or 15 fields, where you have to remember that argument 3 is the first name and argument 7 is the address.

Here is a slightly richer UserBuilder.

class UserBuilder {
  private username: string;
  private email: string;
  private phoneNumber?: string;
  private avatar?: string;

  constructor(username: string, email: string) {
    this.username = username;
    this.email = email;
  }

  setPhoneNumber(phoneNumber: string): UserBuilder {
    this.phoneNumber = phoneNumber;
    return this;
  }

  setAvatar(avatar: string): UserBuilder {
    this.avatar = avatar;
    return this;
  }

  build(): User {
    return new User(this.username, this.email, this.phoneNumber, this.avatar);
  }
}

And this is how you build an object with it (phoneNumber stays unset).

const userFromBuilder: User = new UserBuilder('testUser2', 'userFromBuilder@email.com')
 .setAvatar('avatar')
 .build();

Three things are worth keeping:

  1. The constructor holds only the required fields. It can even be empty.
  2. Every “set” method returns the Builder, so you can chain the properties you actually want.
  3. The User object appears only at the end, when you call build().

← Back to the blog