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:
- The constructor holds only the required fields. It can even be empty.
- Every “set” method returns the Builder, so you can chain the properties you actually want.
- The User object appears only at the end, when you call build().