Skip to main content

The Builder Pattern Real World Implementation

Let's consider a more realistic example of a real-world application that could benefit from the Builder pattern: a customer onboarding system for a financial institution.

In this scenario, the customer onboarding process involves gathering various pieces of information from the customer, such as personal details, contact information, account preferences, and documentation. The Builder pattern can help in constructing the customer object with all the required information in a step-by-step manner.

Reading UML Diagram

  • (-) Sign means that that member is private
  • (+) Sign means that that member is public
  • (Underlined Members) Means that that member is static
  • «abstract» Means that that class is an abstract class
  • «interface» Represents an interface
TypeScript Course Instructor Image
TypeScript Course Instructor Image

Time To Transition From JavaScript To TypeScript

Level Up Your TypeScript And Object Oriented Programming Skills. The only complete TypeScript course on the marketplace you building TypeScript apps like a PRO.

SEE COURSE DETAILS

Real World Implementation Example

Here's an example implementation of the Builder pattern in TypeScript for the customer onboarding system:

interface ICustomer {
firstName: string;
lastName: string;
email: string;
phoneNumber: string;
}

interface ICustomerBuilder {
setFirstName(firstName: string): ICustomerBuilder;
setLastName(lastName: string): ICustomerBuilder;
setEmail(email: string): ICustomerBuilder;
setPhoneNumber(phoneNumber: string): ICustomerBuilder;
build(): ICustomer;
}

class Customer implements ICustomer {
constructor(
public firstName: string,
public lastName: string,
public email: string,
public phoneNumber: string
) {
// Customer constructor logic...
}

// Customer methods...
}

class CustomerBuilder implements ICustomerBuilder {
private firstName: string = "";
private lastName: string = "";
private email: string = "";
private phoneNumber: string = "";

setFirstName(firstName: string): ICustomerBuilder {
this.firstName = firstName;
return this;
}

setLastName(lastName: string): ICustomerBuilder {
this.lastName = lastName;
return this;
}

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

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

build(): ICustomer {
return new Customer(
this.firstName,
this.lastName,
this.email,
this.phoneNumber
);
}
}

class CustomerDirector {
constructor(private builder: ICustomerBuilder) {}

buildMinimal(firstName: string, lastName: string, email: string): ICustomer {
return this.builder
.setFirstName(firstName)
.setLastName(lastName)
.setEmail(email)
.build();
}
}

// Usage example:
const builder: ICustomerBuilder = new CustomerBuilder();
const director: CustomerDirector = new CustomerDirector(builder);
const customer: ICustomer = director.buildMinimal(
"John",
"Doe",
"john.doe@example.com"
);

console.log(customer);

Advantages Of Using The Builder Pattern

The Builder pattern provides several benefits, especially in scenarios where complex objects need to be created and constructed. Using the ConcreteBuilder1 example (Builder pattern applied to a Product), these benefits include:

Fluent Interface

In TypeScript (and many other languages), builders can be used to create a fluent interface. This makes the client code more readable and easy to write. It's especially useful when constructing complex objects step by step.

Separation of Construction Logic and Business Logic

The Builder pattern separates the construction of an object from its representation. In the given example, the logic for building a Product (adding parts and assembling it) is encapsulated within the ConcreteBuilder1 class. This keeps the client code ( which might be in the Director class or elsewhere) focused on the business logic and not distracted by the intricacies of object construction.

Different Representations

By using different ConcreteBuilder classes, you can create different representations of the product without changing the client code. This is because the client code interacts only with the abstract Builder interface and is thus decoupled from the specific classes of objects being created.

Increased Object Integrity

The Builder pattern helps in constructing an object step-by-step, verifying each step before proceeding to the next one. This process can ensure the object constructed is always valid, which leads to higher data integrity.

Immutability

Once the object has been constructed, it's often returned as an immutable object. Immutability has many advantages including, simplicity (since immutable objects can be easily shared or cached), safety ( since they can't be changed once created), and it aids in writing cleaner code.

Reduced Parameter Complexity

Builder pattern is a great way to mitigate the telescoping constructor issue where a constructor with too many parameters becomes hard to manage and understand. Using a Builder pattern you provide a clear and fluent interface to set the required parameters.

Easier to Extend

If you want to add a new feature to the product, you don't need to modify the Director or the client code, but you can extend the Builder class to add this new feature. This makes the system easier to extend and maintain.

What Can You Do Next 🙏😊

If you liked the article, consider subscribing to Cloudaffle, my YouTube Channel, where I keep posting in-depth tutorials and all edutainment stuff for ssoftware developers.

YouTube @cloudaffle