This article is part of a series on design patterns. Following the Creational patterns, the Builder is an extremely useful design pattern for creating objects. So, what is a Builder and how do you implement one? Through this article, we hope to provide everyone with additional knowledge about the Builder pattern.
1. What is the Builder Design Pattern?
In previous articles, I introduced various design patterns in the Creational group, such as Singleton and Factory. Today, I will introduce another design pattern related to these two: the Builder pattern.
First, a little fun fact: our predecessors were quite resourceful and came up with a bunch of patterns for future generations to use, like Singleton, Factory, and now Builder, among many others.
In reality, each pattern serves a specific purpose. Singleton ensures that only one instance is created, and Factory can create multiple types of objects based on certain conditions. However, most developers often deal with objects that have numerous attributes, and creating such objects can be quite challenging. Why is it challenging? And is there a solution to this problem? The answer is the Builder pattern.
Ok, let’s go. Now, let’s analyze the “challenge” and see how the Builder pattern helps us solve these issues.
We’ll use an example based on the Fintech platform. Let’s say we are building a Core Banking system. A specific feature we need to implement is an API to create a BankAccount for SaigonTechnologyBank. Although it is an internationally renowned bank, it only requires information for each account that includes: the customer’s full name, email address, and phone number.
The requirement is quite simple, and we can come up with a solution in no time.
public class BankAccount {
private String fullName;
private String email;
private String phoneNumber;
public BankAccount(String fullName, String email, String phoneNumber){
this.fullName = fullName;
this.email = email;
this.phoneNumber = phoneNumber;
}
}
With SaigonTechnologyBank’s success, more and more other banks want to use our CoreBanking system. However, besides the common information for a BankAccount, each new bank requires additional details like citizen identification, nationality, address, occupation, family information, and more.
Now, the BankAccount class has accumulated quite a few attributes. This means that to create an object, all these attributes must be added to the constructor. Imagine a constructor with more than ten parameters. It would be exhausting, wouldn’t it?
- It’s exhausting because there are too many parameters passed in from the client side, which is also a drawback of using the Factory method.
- It’s tiring because you have to remember the position of each parameter.
- It’s exhausting because many parameters are optional.
The solution is quite simple: create multiple constructors and use the one you need to create the object. However, having many constructors can be confusing. It requires programmers to remember how to use each one or to investigate them separately to choose the best.
Well, in that case, I’ll just create a constructor with the required fields. Then, I’ll use setter() to update information for the optional fields. Great idea, but this approach cannot be applied to immutable objects.
Cry me a river. This method doesn’t work, and that one doesn’t either. But luckily, our predecessors have designed a design pattern called Builder. Just hearing the name inspires confidence. This design helps us overcome the limitations of the aforementioned issues.
2. Builder Design Pattern Diagram
Now, let’s explore various ways to implement the Builder design pattern. The class diagram shows this. Coders can see that implementing the Builder is easy.
The AccountDirector utilizes the BankAccountBuilder to create a BankAccount. Here, let’s assume that the BankAccount has many optional fields.
3. Builder Design Pattern Implementation
Let’s use the diagram. We’ll walk through how to implement this pattern.
To simplify things, we’ll place the BankAccountBuilder class inside the BankAccount class.
public class BankAccount{
private String bankName; // Required field
private String accountNumber;// Required field
private String idNumber; // Required field
private String fullName;
private String email;
private String password;
private String address;
public BankAccount(String bankName, String accountNumber, String idNumber){
this.bankName = bankName;
this.accountNumber = accountNumber;
this.idNumber = idNumber;
}
// Getter() and Setter() should be here
public static class BankAccountBuilder {
private String bankName; // Required field
private String accountNumber; // Required field
private String idNumber; // Required field
private String fullName;
private String email;
private String password;
private String address;
public BankAccountBuilder(String bankName, String accountNumber, String idNumber){
this.bankName = bankName;
this.accountNumber = accountNumber;
this.idNumber = idNumber;
}
public BankAccountBuilder withFullName(String fullName){
this.fullName = fullName;
return this;
}
public BankAccountBuilder withEmail(String email){
this.email = email;
return this;
}
public BankAccountBuilder withPassword(String password){
this.password = password;
return this;
}
public BankAccountBuilder withAddress(String address){
this.address = address;
return this;
}
public BankAccount build() {
// validation() should be here.
BankAccount bankAccount = new BankAccount(this.bankName, this.accountNumber, this.idNumber);
bankAccount.setFullName(this.fullName);
bankAccount.setEmail(this.email);
bankAccount.setPassword(this.password);
bankAccount.setAddress(this.address);
return bankAccount;
}
}
}
Here’s how the AccountDirector class will use the BankAccountBuilder. It will use it to build BankAccount objects.
public class AccountDirector{
public static void main(String[] args){
String bankName = “SaigonTechnologyBank”;
String accountNumber = “STS7777777”;
String idNumber = “7777777”;
BankAccount account = new BankAccount.BankAccountBuilder(bankName, accountNumber, idNumber)
.withFullName(“Wind Hero”)
.withAddress(“MID”)
.build();
System.out.println(account);
}
}
4. Pros and Cons of Builder Design Pattern
Pros of Builder
- The first strength of the Builder pattern is that it helps us control creating objects. They can have many attributes. And, they it is easy.
- It eliminates constructors with many parameters and reduces the number of redundant constructors.
- Builders can assist in creating immutable objects.
StringBuilder and StringBuffer are among the many commonly used Builders provided by Java.
Cons of Builder
- The biggest drawback of Builder is the potential for duplicate code. This is because the Builder class often needs to fully copy all of the Product class’s attributes.
- The Builder class will become increasingly bloated as new attributes are added.
Currently, Java developers can use Lombok. It simplifies creating a Builder.
5. The Conclusion
I’d like to conclude the article here. I’ve outlined what the Builder design pattern is and how to use it. I hope to get contributions from everyone. They will help the programming community. Especially, they will improve future articles.
In conclusion, we have covered the Builder design pattern in Java well. Need Java experts? Contact Saigon Technology today. We can advise and support you on the best solutions for your project.