In the last article, we learned about the first design pattern called the Abstract Factory Pattern. In this article, let’s learn about a new creational design pattern called “The Builder Factory Pattern”.

What is the Builder Pattern?

Builder Pattern is a creational design pattern that lets you construct complex objects step by step. The pattern allows you to produce different types and representations of an object using the same construction code.

When the object has many attributes, there are some main issues that the Factory Pattern and the Abstract Factory Pattern have to deal with:

  • There are too many parameters that must be passed from the client to Factory class.
  • Some parameters may be optional but in Factory Pattern, we must pass values to all of the parameters. With optional parameters, we have no other way than passing a null value to them.

We can solve the above problems by providing a constructor with the required parameters and getter/ setter methods to install optional parameters. However, this approach will cause the object’s state to become inconsistent as all of its attributes are explicitly installed.

Due to that reason, the builder pattern was born. It solves this problem by providing a step-by-step method of constructing the object and providing a method to return the final complete object.

Structure

The structure of the Builder Pattern consists of 5 important components: Builder, Concrete Builder(s), Product(s), Director and Client.

The structure of the Builder Pattern

  • Builder declares product construction steps that are common to all types of builders.
  • Concrete Builder(s) provide different implementations of the construction steps. Concrete builders may produce products that don’t follow the common interface.
  • Product(s) are resulting objects. Products constructed by different builders don’t have to belong to the same class hierarchy or interface.
  • Director defines the order in which to call construction steps, so you can create and reuse specific configurations of products. Using Director is optional.
  • Client must associate one of the builder objects with the director.

When to Use the Builder Pattern?

Here are some cases you should consider using the builder pattern for your code:

  • There are too many cumbersome and complex constructors inside your classes.
  • You don’t want to assign values to the parameters of the constructor to follow a certain fixed order. For example: Instead of having to assign sequential values from parameter A then parameter B and parameter C, you can assign values to parameter B first and then to A and C.

Problem

Imagine you are a part of a software development team working on a piece of software for a bank called XYZ. Among other things, your team will need a way to represent bank accounts. Below is the first version of the BankAccount class that your team created for the first days:

public class BankAccount 
{
   private $accountNumber;
   private $owner;
   private $balance;

   public function __construct($accountNumber, $owner, $balance) 
   {
       $this->accountNumber = $accountNumber;
       $this->owner = $owner;
       $this->balance = $balance;
   }
}

It’s not too hard to understand all of the code above, right? To create a new bank account, what you need to do is reasonably straightforward:

$account = new BankAccount(123, "Bart", 100);

Unfortunately, a few weeks after your team’s first version of the BankAccount class, a new requirement arrived that required the addition of the monthly interest rate applicable to each account, the date on which it was opened, and, optionally, the branch at which it was opened. It sounded easy enough, so your team got down to work immediately and then came up with the second version of the BankAccount class:

public class BankAccount 
{
    private $accountNumber;
    private $owner;
    private $branch;
    private $balance;
    private $interestRate;

    public function __construct($accountNumber, $owner, $branch, $balance, $interestRate) 
    {
        $this->accountNumber = $accountNumber;
        $this->owner = $owner;
        $this->branch = $branch;
        $this->balance = $balance;
        $this->interestRate = $interestRate;
    }
}

After the second update, it is not too difficult to create a new bank account:

BankAccount account = new BankAccount(456, "Marge", "Springfield", 100, 2.5);
BankAccount anotherAccount = new BankAccount(789, "Homer", null, 2.5, 100);

The compiler still compiles and executes the code above normally. However, do you notice any unusual points in the above code? If paying close attention to it, you will easily realize that the balance and interestRate value of the bank account that belongs to Homer have been mistakenly assigned to each other. It is not difficult to realize that Homer’s money will double after each month with 100% interest (balance = 2.5; interestRate = 100). (If you knows such a bank with an ideal interest rate like this, please let me know).

The above problem shows that if the constructor has many consecutive parameters of the same data type, mistaking the value of this parameter to the value of another parameter is unavoidable. Since the compiler doesn’t recognize it as an error, it is quite difficult to detect and correct these errors when dealing with complex programs.

In addition, cramming the constructor with too many parameters will make our code more difficult to read. With a number of 10, 20 or 30 parameters, it is pretty difficult to identify what’s inside the constructor at a single glance.

Worse, some parameters may be optional, which means we will have to create extra constructors or will have to assign null values to unnecessary parameters.

Some people propose that we can solve this problem by calling a constructor without any parameters. Then, set the value of its attributes via alternative setter methods. However, this solution gives us another problem – what if a programmer forgets to call a setter method of a particular attribute and this attribute is a compulsory one? As a result, we will create a new object. That is only partially initialized and once again, the compiler will not detect any problem with it.

That’s the reason why Builder Pattern was born to save the world.

Solution

The Builder Pattern is a creational pattern that solves the problem of initializing complex objects by providing a way to construct objects step by step and provide a method to returns the final complete object.

With the problem set, we will proceed to build the Builder class containing all the attributes of class BankAccount. Besides, we will only use this Builder class to initialize a new object instead of using the constructor of class BankAccount like before.

Apply the Pattern Builder to solve our problem, here is all that we will get:

public interface Builder
{
     public function setAccountNumber($accountNumber);
     public function setOwner($owner);
     public function setBranch($branch);
     public function setBalance($balance);
     public function setInterestRate($interestRate);
     public function build();
}

public class BankAccountBuilder implements Builder
{
     private $accountNumber;
     private $owner;
     private $branch;
     private $balance;
     private $interestRate;

     public function setAccountNumber($accountNumber)
     {  
          $this->accountNumber = $accountNumber;
          return $this;
     }

     public function setOwner($owner)
     {
          $this->owner = $owner;
          return $this;
     } 

     public function setBranch($branch)
     {
          $this->branch = $branch;
          return $this;
     }

     public function setBalance($balance)
     {
          $this->balance = $balance;
          return $this;
     }

     public function setInterestRate($interestRate)
     {
          $this->interestRate = $interestRate;
          return $this;
     }

     public function build()
     {
          return new BankAccount($this->accountNumber, $this->owner, $this->branch, $this->balance, $this->interestRate);
     }
}

public class BankAccount
{
     private $accountNumber;
     private $owner;
     private $branch;
     private $balance;
     private $interestRate;

     public function __construct($accountNumber, $owner, $branch, $balance, $interestRate)
     {
          $this->accountNumber = $accountNumber;
          $this->owner = $owner;
          $this->branch = $branch;
          $this->balance = $balance;
          $this->interestRate = $interestRate;
     }

     public function getAccountNumber()
     {
          return $this->accountNumber;
     }

     public function getOwner()
     {
          return $this->owner;
     }

     public function getBranch()
     {
          return $this->branch;
     }

     public function getBalance()
     {
          return $this->balance;
     }

     public function getInterestRate()
     {
          return $this->interestRate;
     }

     public function showInfo()
     {
          echo "XYZ Bank – Account Information";
          echo "\n";
          if (!empty( (string) $this->getAccountNumber() ))
               echo "* Number: " . $this->getAccountNumber();
          if (!empty( (string) $this->getOwner() ))
               echo "* Owner: " . $this->getOwner();
          if (!empty( (string) $this->getBranch() ))
               echo "* Branch: " . $this->getBranch();
          if (!empty( (string) $this->getBalance() ))
               echo "* Balance: " . $this->getBalance();
          if (!empty( (string) $this->getInterestRate() ))
               echo "* Interest Rate: " . 
$this->getInterestRate();
     }
}

$bankAccount = new BankAccountBuilder().setOwner("Homer").setBalance(100.00).setInterestRate(2.5).build();
$bankAccount.showInfo();

That’s it! We have completed setting up the Builder Pattern to solve our problem. Let’s execute the code below to see the final result:

XYZ Bank – Account Information;
* Number: 789
* Owner: Homer
* Balance: 100.00
* Interest Rate: 2.5

By using the Builder Pattern, we can see that creating a new object has now become much easier and more intuitive than before. It’s pretty awesome, right?

The Pros and Cons of the Builder Pattern

Pros

  • The Builder Pattern helps users avoid cramming their class with dozens of constructors.
  • The Builder Pattern helps you construct objects step-by-step, defer construction steps or run steps recursively.
  • With the Builder Pattern, users don’t need to pass null values to parameters that the object doesn’t need anymore.
  • The Builder Pattern helps your object creational code less error-prone as users will know what they are passing because of explicit method call.
  • With Builder Pattern, we can better control the building process of the object: we can add some code to validate the object before it is created and returned to the client.
  • The Builder Pattern helps your code become much easier to read and maintain in the future.
  • By using the Builder Pattern, you can isolate complex construction code from the business logic of the product.

Cons

  • The code may become much more complicated since you will need to introduce a lot of new subclasses to implement the pattern.

Conclusion

In this article, we have learned a lot about the Builder Pattern. I hope that this design pattern will be a perfect solution to one of your software development problems sometime in the future.

In the next article of this series, we will learn about another creational design pattern called Prototype Pattern. Stay tuned!

Leave a Reply