Builder Pattern in C++


Reading time: 35 minutes

Builder pattern in C++ aims to “Separate the construction of a complex object from its representation so that the same construction process can create different representations.” It is used to construct a complex object step by step and the final step will return the object. The process of constructing an object should be generic so that it can be used to create different representations of the same object.

topic of image

Product – The product class defines the type of the complex object that is to be generated by the builder pattern.

Builder – This abstract base class defines all of the steps that must be taken in order to correctly create a product. Each step is generally abstract as the actual functionality of the builder is carried out in the concrete subclasses. The GetProduct method is used to return the final product. The builder class is often replaced with a simple interface.

ConcreteBuilder – There may be any number of concrete builder classes inheriting from Builder. These classes contain the functionality to create a particular complex product.

Director – The director class controls the algorithm that generates the final product object. A director object is instantiated and its Construct method is called. The method includes a parameter to capture the specific concrete builder object that is to be used to generate the product. The director then calls methods of the concrete builder in the correct order to generate the product object. On completion of the process, the GetProduct method of the builder object can be used to return the product.

Why we need Builder pattern?

Consider a construction of a home. Home is the final end product (object) that is to be returned as the output of the construction process. It will have many steps like basement construction, wall construction and so on roof construction.

Example without Builder pattern

The example written below is withour builder pattern hence the number of copies of the same code will be redundant and uneccesssary. Moreover a new builder will be created for every product which will further affect the perfomance of code.

//Without builder design
class HousePlan 
{ 
    public void setBasement(String basement); 
  
    public void setStructure(String structure); 
  
    public void setRoof(String roof); 
  
    public void setInterior(String interior); 
} 
  
class House : public HousePlan 
{ 
  
    private String basement; 
    private String structure; 
    private String roof; 
    private String interior; 
  
    public void setBasement(String basement)  
    { 
        this.basement = basement; 
    } 
  
    public void setStructure(String structure)  
    { 
        this.structure = structure; 
    } 
  
    public void setRoof(String roof)  
    { 
        this.roof = roof; 
    } 
  
    public void setInterior(String interior)  
    { 
        this.interior = interior; 
    } 
  
} 
  
  
class HouseBuilder 
{ 
  
    public void buildBasement(); 
  
    public void buildStructure(); 
  
    public void bulidRoof(); 
  
    public void buildInterior(); 
  
    public House getHouse(); 
} 
  
class IglooHouseBuilder : public HouseBuilder 
{ 
    private House house; 
  
    public IglooHouseBuilder()  
    { 
        this.house = new House(); 
    } 
  
    public void buildBasement()  
    { 
        house.setBasement("Ice Bars"); 
    } 
  
    public void buildStructure()  
    { 
        house.setStructure("Ice Blocks"); 
    } 
  
    public void buildInterior()  
    { 
        house.setInterior("Ice Carvings"); 
    } 
  
    public void bulidRoof()  
    { 
        house.setRoof("Ice Dome"); 
    } 
  
    public House getHouse()  
    { 
        return this.house; 
    } 
} 
  
class TipiHouseBuilder : public HouseBuilder 
{ 
    private House house; 
  
    public TipiHouseBuilder()  
    { 
        this.house = new House(); 
    } 
  
    public void buildBasement()  
    { 
        house.setBasement("Wooden Poles"); 
    } 
  
    public void buildStructure()  
    { 
        house.setStructure("Wood and Ice"); 
    } 
  
    public void buildInterior()  
    { 
        house.setInterior("Fire Wood"); 
    } 
  
    public void bulidRoof()  
    { 
        house.setRoof("Wood, caribou and seal skins"); 
    } 
  
    public House getHouse()  
    { 
        return this.house; 
    } 
  
} 
  
class CivilEngineer  
{ 
  
    private HouseBuilder houseBuilder; 
  
    public CivilEngineer(HouseBuilder houseBuilder) 
    { 
        this.houseBuilder = houseBuilder; 
    } 
  
    public House getHouse() 
    { 
        return this.houseBuilder.getHouse(); 
    } 
  
    public void constructHouse() 
    { 
        this.houseBuilder.buildBasement(); 
        this.houseBuilder.buildStructure(); 
        this.houseBuilder.bulidRoof(); 
        this.houseBuilder.buildInterior(); 
    } 
} 
  
class Builder 
{ 
    public static void main(String[] args) 
    { 
        HouseBuilder iglooBuilder = new IglooHouseBuilder(); 
        CivilEngineer engineer = new CivilEngineer(iglooBuilder); 
  
        engineer.constructHouse(); 
  
        House house = engineer.getHouse(); 
  
        System.out.println("Builder constructed: "+ house); 
    } 
}

Example with Builder pattern

The example below uses builder pattern hence is more convient and easy to understand as well as removing the the difficultities mentioned in the above program are removed. Hence builder pattern is used in the remaining article you will also see the advantages and disadvantages for using thia pattern.

//With builder design
interface HousePlan 
{ 
    public void setBasement(String basement); 
  
    public void setStructure(String structure); 
  
    public void setRoof(String roof); 
  
    public void setInterior(String interior); 
} 
  
class House implements HousePlan 
{ 
  
    private String basement; 
    private String structure; 
    private String roof; 
    private String interior; 
  
    public void setBasement(String basement)  
    { 
        this.basement = basement; 
    } 
  
    public void setStructure(String structure)  
    { 
        this.structure = structure; 
    } 
  
    public void setRoof(String roof)  
    { 
        this.roof = roof; 
    } 
  
    public void setInterior(String interior)  
    { 
        this.interior = interior; 
    } 
  
} 
  
  
interface HouseBuilder 
{ 
  
    public void buildBasement(); 
  
    public void buildStructure(); 
  
    public void bulidRoof(); 
  
    public void buildInterior(); 
  
    public House getHouse(); 
} 
  
class IglooHouseBuilder implements HouseBuilder 
{ 
    private House house; 
  
    public IglooHouseBuilder()  
    { 
        this.house = new House(); 
    } 
  
    public void buildBasement()  
    { 
        house.setBasement("Ice Bars"); 
    } 
  
    public void buildStructure()  
    { 
        house.setStructure("Ice Blocks"); 
    } 
  
    public void buildInterior()  
    { 
        house.setInterior("Ice Carvings"); 
    } 
  
    public void bulidRoof()  
    { 
        house.setRoof("Ice Dome"); 
    } 
  
    public House getHouse()  
    { 
        return this.house; 
    } 
} 
  
class TipiHouseBuilder implements HouseBuilder 
{ 
    private House house; 
  
    public TipiHouseBuilder()  
    { 
        this.house = new House(); 
    } 
  
    public void buildBasement()  
    { 
        house.setBasement("Wooden Poles"); 
    } 
  
    public void buildStructure()  
    { 
        house.setStructure("Wood and Ice"); 
    } 
  
    public void buildInterior()  
    { 
        house.setInterior("Fire Wood"); 
    } 
  
    public void bulidRoof()  
    { 
        house.setRoof("Wood, caribou and seal skins"); 
    } 
  
    public House getHouse()  
    { 
        return this.house; 
    } 
  
} 
  
class CivilEngineer  
{ 
  
    private HouseBuilder houseBuilder; 
  
    public CivilEngineer(HouseBuilder houseBuilder) 
    { 
        this.houseBuilder = houseBuilder; 
    } 
  
    public House getHouse() 
    { 
        return this.houseBuilder.getHouse(); 
    } 
  
    public void constructHouse() 
    { 
        this.houseBuilder.buildBasement(); 
        this.houseBuilder.buildStructure(); 
        this.houseBuilder.bulidRoof(); 
        this.houseBuilder.buildInterior(); 
    } 
} 
  
class Builder 
{ 
    public static void main(String[] args) 
    { 
        HouseBuilder iglooBuilder = new IglooHouseBuilder(); 
        CivilEngineer engineer = new CivilEngineer(iglooBuilder); 
  
        engineer.constructHouse(); 
  
        House house = engineer.getHouse(); 
  
        System.out.println("Builder constructed: "+ house); 
    } 
} 

Output :

Builder constructed: House@6d06d69c

Advantages and Disadvantages

Advantages

  1. The parameters to the constructor are reduced and are provided in highly readable method calls.
  2. Builder design pattern also helps in minimizing the number of parameters in constructor and thus there is no need to pass in null for optional parameters to the constructor.
  3. Object is always instantiated in a complete state.
  4. Immutable objects can be build without much complex logic in object building process.

Disadvantages

  1. The number of lines of code increase at least to double in builder pattern, but the effort pays off in terms of design flexibility and much more readable code.
  2. Requires creating a separate ConcreteBuilder for each different type of Product.