Design Pattern: Builder Pattern

What is it?

According to Gang of Four, “Separates the construction of a complex object from its representation so that the same construction process can create different representations.” Again, no idea reading it for the first time 🙂

So, stated simply, we try to separate the construction of a complex object from its representation to reuse the building process for creating different representations.

Let’s take an example of subway, where we provide all the details of vegetables and sauces needed (data) and we get the sandwich (complex object). Here, we did not tell how to prepare it (building process). This is what we want to do with object creation process too.

In software world:

To create a sandwich object, you pass all the parameters in the constructor to create the object. This can get worse if we have multiple constructors with different parameters.

Improvement #1:

Remove the parameters from the constructor and set all the properties you need. Something like:

 Sandwich sandwich = new Sandwich();
 sandwich.setBreadType(BreadType.WHITE);
 sandwich.setHasMayo(true); ...

But, now we need to check if we have set all the properties. We have no way to set the order. I mean, you need to provide the bread type before vegetables and sauce you need that can not be controlled anymore. So, we need a builder that takes all details and build it using proper steps.

Improvement #2:

class SandwichBuilder{
       Sandwich sandwich;

       public Sandwich getSandwich(){
             return sandwich; 
       }

       public void createSandwich(){
              sandwich = new Sandwich();
              sandwich.setBreadType(BreadType.WHITE);
              .... 
       }
}

It can be further modified to modularize and make the building process more meaningful with functions doing smaller tasks being called from createSandwich

       public void createSandwich(){
              sandwich = new Sandwich();
              selectBreadType();
              addVegetables();
              addSauces();
              .... 
       }

So, now we have a builder whose object can be used to create a new sandwich having same properties using this builder.

Also, this helps when creation of the complex object (sandwich here) changes i.e. we can change the code inside the build function (createSandwich here).

One more advantage here is if we have more than one kind of complex object (different types of sandwich), just create a new builder class to handle that and we can reuse the creation of the complex object de-coupling it from its representation.

But, now we need to create multiple builder classes with similar functions without any relation between them (no common interface implemented by them) and much of code will be replicated in these builders.

Improvement #3

Create an abstract class controlling the structure of the builder which can be used by each builder to use the same procedure with different values (ingredients here):

abstract class SandwichBuilder{
        Sandwich sandwich;

        public Sandwich getSandwich(){
               return sandwich;
        }

        public void createNewSandwich(){
               sandwich=new Sandwich();
        }

        public abstract void addVegetables();
        public abstract void addSauces();
        ....
}

So, this class here now encapsulates the commonalities between the builders but who handles the procedure of building the sandwich.

It should not be added here as this is the class that does the work common to all the builders. Here is what we do to mitigate this:

class SandwichMaker{
      private SandwichBuilder builder;

      public SandwichMaker(SandwichBuilder builder){
             this.builder = builder;
      }

      public void buildSandwich(){
             builder.createNewSandwich();
             builder.selectBread();
             builder.addVegetables();
             builder.addSauces();
      }

      public Sandwich getSandwich(){
             return builder.getSandwich();
      }
}

So, now our builder class just handles the properties (ingredients here) to create the complex object and the creation steps are handled by the maker and your code using this will look something like this:

      SandwichMaker maker = new SandwichMaker(new MyFavSandwichBuilder());
      maker.buildSandwich();
      Sandwich sandwich = maker.getSandwich();

The Builder Pattern Design:

Director (SandwichMaker) <>——- Builder (SandwichBuilder) <———- ConcreteBuilder (MyFavSandwichBuilder) ——-> Product (Sandwich)

So in Hollywood terms:

Director knows how to build

ConcreteBuilder defines the steps for creation of the Product used by Director

Where to use:

  • Need to create a complex object with lot of details embedded into it.
  • Multiple data combination to create multiple types of products using same steps.
Advertisements