Builder Design Pattern

Builder Design Pattern

In this tutorial, we are going to discuss about the Builder Design Pattern. The builder design pattern, as name implies, is an alternative way to construct complex objects. Builder Design Pattern is one of the Creational Design Pattern. This design pattern was introduced to solve some of the problems with Factory and Abstract Factory design patterns when the Object contains a lot of attributes.

Builder Design Pattern is particularly useful when an object needs to be created with many possible configurations and combinations of its components.

Real-Life Example

Builder Design Pattern

To give you an idea of how this pattern works in the real world, let’s consider building a house. You wouldn’t start construction without a detailed plan, right? You’d need to figure out things like how many rooms you want, what type of materials to use, and how everything should be laid out. Similarly, the Builder Pattern can serve as a blueprint for your software object if it has lots of parameters, especially if many of them are optional.

There are 3 major issues with Factory and Abstract Factory design patterns when the Object contains a lot of attributes.

  1. Too Many arguments to pass from client program to the Factory class that can be error prone because most of the time, the type of arguments are same and from client side its hard to maintain the order of the argument.
  2. Some of the parameters might be optional but in Factory pattern, we are forced to send all the parameters and optional parameters need to send as NULL.
  3. If the object is heavy and its creation is complex, then all that complexity will be part of Factory classes that is confusing.

We can solve the issues with large number of parameters by providing a constructor with required parameters and then different setter methods to set the optional parameters. The problem with this approach is that the Object state will be inconsistent until unless all the attributes are set explicitly. Builder design pattern solves the issue with large number of optional parameters and inconsistent state by providing a way to build the object step-by-step and provide a method that will actually return the final Object.

How to implement builder design pattern
  1. First of all you need to create a static nested class and then copy all the arguments from the outer class to the Builder class. We should follow the naming convention and if the class name is Computer then builder class should be named as ComputerBuilder.
  2. Java Builder class should have a public constructor with all the required attributes as parameters.
  3. Java Builder class should have methods to set the optional parameters and it should return the same Builder object after setting the optional attribute.
  4. The final step is to provide a build() method in the builder class that will return the Object needed by client program. For this we need to have a private constructor in the Class with Builder class as argument.

Let’s discuss a common problem in our application. Assume, Computer object has following 4 attributes i.e. HDD, RAM, isGraphicsEnabled and isBluetoothEnabled. In normal practice, if you want to make a immutable Computer class, then you must pass all 4 information as parameters to constructor. It will look like this

public Computer(String HDD, String RAM, boolean isGraphicsCardEnabled, boolean isBluetoothEnabled) {
   this.HDD = HDD;
   this.RAM = RAM;
   this.isGraphicsCardEnabled = isGraphicsCardEnabled;
   this.isBluetoothEnabled =  isBluetoothEnabled;
}

Now what if only HDD and RAM are mandatory and rest 2 fields are optional. Problem !! We need more constructors.

public Computer(String HDD, String RAM, boolean isGraphicsCardEnabled) { .... }
public Computer(String HDD, String RAM, boolean isBluetoothEnabled) { .... }
public Computer(String HDD, String RAM) { .... }

We will need some more like above. Still can manage? Now let’s introduce our fifth attribute then is problem. To solve this problem we can go for Builder design pattern.

package com.ashok.designpatterns.builder;

/**
 * 
 * @author ashok.mariyala
 *
 */
public class MyComputer {

   // Required parameters
   private String HDD;
   private String RAM;

   // Optional parameters
   private boolean isGraphicsEnabled;
   private boolean isBluetoothEnabled;

   public String getHDD() {
      return HDD;
   }

   public String getRAM() {
      return RAM;
   }

   public boolean isGraphicsEnabled() {
      return isGraphicsEnabled;
   }

   public boolean isBluetoothEnabled() {
      return isBluetoothEnabled;
   }

   private MyComputer(MyComputerBuilder builder) {
      this.HDD = builder.HDD;
      this.RAM = builder.RAM;
      this.isGraphicsEnabled = builder.isGraphicsEnabled;
      this.isBluetoothEnabled = builder.isBluetoothEnabled;
   }

   // Builder Class
   public static class MyComputerBuilder {

      // Required parameters
      private String HDD;
      private String RAM;

      // Optional parameters
      private boolean isGraphicsEnabled;
      private boolean isBluetoothEnabled;

      public MyComputerBuilder(String hdd, String ram) {
         this.HDD = hdd;
         this.RAM = ram;
      }

      public MyComputerBuilder setGraphicsCardEnabled(boolean isGraphicsCardEnabled) {
         this.isGraphicsEnabled = isGraphicsCardEnabled;
         return this;
      }

      public MyComputerBuilder setBluetoothEnabled(boolean isBluetoothEnabled) {
         this.isBluetoothEnabled = isBluetoothEnabled;
         return this;
      }

      public MyComputer build() {
         return new MyComputer(this);
      }
   }
}

Notice that MyComputer class has only getter methods and no public constructor. So the only way to get a MyComputer object is through the MyComputerBuilder class. Here is a builder pattern example test program showing how to use Builder class to get the object.

package com.ashok.designpatterns.builder;

import com.ashok.designpatterns.builder.MyComputer;

/**
 * 
 * @author ashok.mariyala
 *
 */
public class MyBuilderPattern {
   public static void main(String[] args) {
      MyComputer comp = new MyComputer.MyComputerBuilder("500 GB", "8 GB")
            .setBluetoothEnabled(false).setGraphicsCardEnabled(true).build();
      System.out.println(comp.getHDD());
      System.out.println(comp.getRAM());
      System.out.println(comp.isBluetoothEnabled());
      System.out.println(comp.isGraphicsEnabled());      
   }
}

Output

500 GB
8 GB
false
true
Summary

The Builder Design Pattern is ideal for constructing complex objects, especially when the construction process must allow different representations or configurations of the object. It provides clarity and flexibility in how objects are constructed and is particularly useful in cases where objects need to be created with a large number of optional components or configurations. However, greater complexity and the possibility of duplicate code come at the expense of this flexibility and control.

That’s all about the Builder Design Pattern. If you have any queries or feedback, please write us email at contact@waytoeasylearn.com. Enjoy learning, Enjoy Design patterns.!!

Builder Design Pattern
Scroll to top