Code360 powered by Coding Ninjas X Naukri.com. Code360 powered by Coding Ninjas X Naukri.com
Table of contents
1.
Introduction
2.
What is Builder Design Pattern?
3.
When Will We Need Builder Design Pattern?
4.
Working of Builder Design Pattern
4.1.
Structure
4.1.1.
Builder
4.1.2.
Product
4.2.
Implementation
4.2.1.
Pseudocode
4.2.2.
Customer with pseudocode
5.
UML Diagram of Builder Design Pattern
6.
Language Specific Code
6.1.
Java Code
6.2.
C++ Code
6.3.
Python Code
7.
Advantages of Builder Design Pattern
8.
Disadvantages of Builder Design Pattern
9.
Difference between Builder Design Pattern and Factory Design Pattern
10.
Frequently Asked Questions
10.1.
What is the real life use case of the builder design pattern?
10.2.
What does a builder do in Java?
10.3.
What is the difference between builder pattern and Factory pattern?
10.4.
What is the builder pattern in API?
11.
Conclusion
Last Updated: Mar 27, 2024
Medium

Builder Design Pattern

Author Rinki Deka
0 upvote
Crack Google SDE interview : Essential projects
Speaker
Saurav Prateek
SDE-2 @
20 Jun, 2024 @ 01:30 PM

Introduction

The Builder Design Pattern is a unique design pattern designed to provide a flexible solution to various object creation problems in object-oriented programming. It's particularly useful when an object needs to be created with a lot of options and configurations. 

Builder design pattern.

This article will delve into the intricacies of the Builder Design Pattern, providing a comprehensive guide to its implementation and use cases.

What is Builder Design Pattern?

The Builder Design Pattern is a creational design pattern that provides a way to construct complex objects step by step. It allows the construction of different representations of an object using the same construction process. The pattern separates the construction of a complex object from its representation, allowing the same construction process to create different representations.

Get the tech career you deserve, faster!
Connect with our expert counsellors to understand how to hack your way to success
User rating 4.7/5
1:1 doubt support
95% placement record
Akash Pal
Senior Software Engineer
326% Hike After Job Bootcamp
Himanshu Gusain
Programmer Analyst
32 LPA After Job Bootcamp
After Job
Bootcamp

When Will We Need Builder Design Pattern?

The Builder Design Pattern is essential when constructing complex objects step by step. It is particularly beneficial when an object must be created with many optional components or configurations. It's also useful when a product contains a lot of data, some of which may or may not be required. The Builder Design Pattern is particularly useful in the following scenarios:

  • Complex Object Construction: When the construction of an object involves multiple steps, configurations, or optional features, and the process needs to be abstracted.
  • Variety of Object Representations: When there is a need to create different representations of an object using the same construction process. The Builder pattern allows flexibility in constructing various versions of an object.
  • Step-by-Step Construction: When the construction of an object follows a step-by-step process, and clients may need to customize or skip certain steps based on their requirements.
  • Telescoping Constructors Issue: To avoid the telescoping constructors anti-pattern, where a class has multiple constructors with different parameter combinations, leading to code that is hard to maintain and understand.
  • Product Immutability: When you want to ensure that the constructed object remains immutable once it's created. The Builder pattern allows constructing an immutable object gradually.
  • Separation of Concerns: When there is a need to separate the construction of an object from its representation, providing clear separation of concerns and improving code readability.

Working of Builder Design Pattern

The Builder Pattern works by separating the construction of a complex object from its representation, allowing the same construction process to create different representations. It typically involves a 'Director' that constructs an object using the 'Builder' interface.

Structure

The structure of the Builder Pattern includes:

  • Builder: Provides an interface for creating parts of a Product object.
     
  • Concrete Builder: Implements the Builder interface and assembles the parts to construct the product.
     
  • Director: Constructs an object using the Builder interface.
     
  • Product: Represents the complex object being built.

Builder

The Builder interface declares product construction steps that are common to all types of builders.

public interface Builder {
    void buildPartA();
    void buildPartB();
    void buildPartC();
}

Product

The Product class represents the complex object that is being built. It's usually a class with multiple fields/attributes.

public class Product {
    private String partA;
    private String partB;
    private String partC;
    // Getters and setters for parts...
}

Implementation

Pseudocode

class Director {
    method construct(builder: Builder) {
        builder.buildPartA()
        builder.buildPartB()
        builder.buildPartC()
    }
}


interface Builder {
    method buildPartA()
    method buildPartB()
    method buildPartC()
    method getResult(): Product
}


class ConcreteBuilder implements Builder {
    private product: Product


    method buildPartA() {
        // implementation...
    }


    method buildPartB() {
        // implementation...
    }


    method buildPartC() {
        // implementation...
    }


    method getResult(): Product {
        return product
    }
}


class Client {
    method main() {
        director = new Director()
        builder = new ConcreteBuilder()
        director.construct(builder)
        product = builder.getResult()
    }
}


Customer with pseudocode

class Customer {
    private String name;
    private String address;
    // Other customer fields and methods...
}
Customer Builder with pseudocode


interface CustomerBuilder {
    method setName(name: String)
    method setAddress(address: String)
    method getResult(): Customer
}
Concrete Customer Builder with pseudocode


class ConcreteCustomerBuilder implements CustomerBuilder {
    private customer: Customer


    method setName(name: String) {
        customer.name = name
    }


    method setAddress(address: String) {
        customer.address = address
    }


    method getResult(): Customer {
        return customer
    }
}


Client with pseudocode

class Client {
    method main() {
        customerBuilder = new ConcreteCustomerBuilder()
        customerBuilder.setName("John Doe")
        customerBuilder.setAddress("123 Elm Street")
        customer = customerBuilder.getResult()
    }
}

Explanation

The pseudocode above illustrates the Builder Pattern's typical use. The Director class orchestrates the building process, which can be customized by passing different Builder implementations. The ConcreteBuilder class constructs and assembles parts of the product, which can be retrieved using the getResult method.

UML Diagram of Builder Design Pattern

The UML (Unified Modeling Language) diagram for the Builder Design Pattern typically includes the following elements:

  • Product: Represents the complex object being constructed.
  • Builder: Abstract interface declaring the construction steps for the product.
  • ConcreteBuilder: Implements the Builder interface, providing specific implementations for constructing parts of the product.
  • Director: Manages the construction process using a builder, orchestrating the steps required to construct the product.
UML Diagram of Builder Design Pattern

Language Specific Code

Below are language-specific examples of implementing the Builder Design Pattern for a hypothetical Car object, which can have various features and parts.

  • Java Code
  • C++ Code
  • Python Code

Java Code

import java.util.*;
public class Car {

private String engine;

private int wheels;

private String color;


// Getters and Setters

public String getEngine() { return engine; }

public void setEngine(String engine) { this.engine = engine; }

public int getWheels() { return wheels; }

public void setWheels(int wheels) { this.wheels = wheels; }

public String getColor() { return color; }

public void setColor(String color) { this.color = color; }

@Override

public String toString() {

return "Car [engine=" + engine + ", wheels=" + wheels + ", color=" + color + "]";

}

}

// The 'Builder' interface

public interface CarBuilder {

CarBuilder setEngine(String engine);

CarBuilder setWheels(int wheels);

CarBuilder setColor(String color);

Car build();

}

// The 'ConcreteBuilder' class

public class ConcreteCarBuilder implements CarBuilder {

private Car car;

public ConcreteCarBuilder() {

this.car = new Car();

}

@Override

public CarBuilder setEngine(String engine) {

car.setEngine(engine);

return this;

}

@Override

public CarBuilder setWheels(int wheels) {

car.setWheels(wheels);

return this;

}
@Override

public CarBuilder setColor(String color) {

car.setColor(color);

return this;

}




@Override

public Car build() {

return car;

}

}

// The 'Director' class

public class CarDirector {

public Car constructSportsCar(CarBuilder builder) {

return builder.setEngine("V8").setWheels(4).setColor("Red").build();

}

}

// The 'Client' class

public class Client {

public static void main(String[] args) {

CarBuilder builder = new ConcreteCarBuilder();

CarDirector director = new CarDirector();

Car car = director.constructSportsCar(builder);

System.out.println(car);

}

}

C++ Code

#include <iostream>

#include <string>

// The 'Product' class

class Car {

private:

std::string engine;

int wheels;

std::string color;

public:

void setEngine(const std::string& eng) { engine = eng; }

void setWheels(int w) { wheels = w; }

void setColor(const std::string& col) { color = col; }

void show() {

std::cout <<"Car [ engine="<< engine <<", wheels=" << wheels<<", color=" << color<<"]"<<std::endl;
}

};

// The 'Builder' interface

class CarBuilder {

public:

virtual CarBuilder* setEngine(const std::string& engine) = 0;

virtual CarBuilder* setWheels(int wheels) = 0;

virtual CarBuilder* setColor(const std::string& color) = 0;

virtual Car* build() = 0;

};
// The 'ConcreteBuilder' class

class ConcreteCarBuilder : public CarBuilder {

private:

Car* car;

public:

ConcreteCarBuilder() { car = new Car(); }

~ConcreteCarBuilder() { delete car; }

CarBuilder* setEngine(const std::string& engine) override {

car->setEngine(engine);

return this;

}

CarBuilder* setWheels(int wheels) override {

car->setWheels(wheels);

return this;

}

CarBuilder* setColor(const std::string& color) override {

car->setColor(color);

return this;

}

Car* build() override {

return car;

}

};

// The 'Director' class

class CarDirector {

public:

Car* constructSportsCar(CarBuilder* builder) {

return builder->setEngine("V8")->setWheels(4)->setColor("Red")->build();
}

};

// The 'Client' code

int main() {

CarBuilder* builder = new ConcreteCarBuilder();

CarDirector director;

Car* car = director.constructSportsCar(builder);

car->show();

delete builder; // Clean up

delete car; // Clean up

return 0;

}

Python Code

# The 'Product' class

class Car:

def __init__(self):

self.engine = None

self.wheels = None

self.color = None

def __str__(self):

return f'Car [engine={self.engine}, wheels={self.wheels}, color={self.color}]'

# The 'Builder' interface

class CarBuilder:

def set_engine(self, engine):

pass

def set_wheels(self, wheels):

pass

def set_color(self, color):

pass

def build(self):

pass

# The 'ConcreteBuilder' class

class ConcreteCarBuilder(CarBuilder):

def __init__(self):

self.car = Car()

def set_engine(self, engine):

self.car.engine = engine

return self

def set_wheels(self, wheels):

self.car.wheels = wheels

return self

def set_color(self, color):

self.car.color = color

return self

def build(self):

return self.car

# The 'Director' class

class CarDirector:

@staticmethod

def construct_sports_car(builder):

return builder.set_engine('V8').set_wheels(4).set_color('Red').build()

# The 'Client' code

if __name__ == '__main__':

builder = ConcreteCarBuilder()

director = CarDirector()

car = director.construct_sports_car(builder)

print(car)

Output 

output

Advantages of Builder Design Pattern

  • Flexibility in Object Creation: Allows for constructing objects step-by-step.
  • Immutability: Once built, objects can be made immutable without the need to have a constructor with too many parameters.
  • Readability: The final code is often more readable and understandable.

Disadvantages of Builder Design Pattern

  • Complexity: Can complicate the codebase with multiple additional classes and interfaces.
     
  • Development Time: More time-consuming to set up compared to simple constructors.

Difference between Builder Design Pattern and Factory Design Pattern

Here's a comparison of the Builder Design Pattern and the Factory Design Pattern in a tabular format:

Aspect Builder Design Pattern Factory Design Pattern
Purpose Creates complex objects step by step, with the flexibility to configure them in a variety of ways. Creates objects, but abstracts the object creation process, returning instances of predefined types or classes.
Object Creation Gradual construction, allows setting optional parameters, creating complex instances with custom configurations. Instantiates objects in a single step, returns fully initialized objects according to specific types or classes.
Complexity Suitable for complex object creation, often with multiple optional parameters and configurations. Appropriate for simpler object creation, often with a single initialization process and predefined types.
Method Chaining Typically employs method chaining to set various attributes and properties during object construction. Usually doesn't involve method chaining for configuration; instead, parameters are passed directly to factory methods.
Flexibility Provides high flexibility in configuring object properties, making it suitable for complex scenarios. Offers less flexibility compared to the Builder pattern, as it focuses on creating predefined objects.
Examples Commonly used in scenarios like building HTML elements, creating custom reports, or constructing complex database queries. Found in situations where multiple classes or subclasses share a common interface or when different object types need to be created from a factory.
Design Considerations Well-suited for cases where object creation requires multiple steps and dynamic configurations. Appropriate for scenarios where object instantiation is straightforward and involves creating instances of predefined classes or types.

Frequently Asked Questions

What is the real life use case of the builder design pattern?

The real life use case of the builder design pattern is building a complex object, like an email with optional attachments and formatting, where different configurations are needed.

What does a builder do in Java?

In Java, a builder is a design pattern used to construct and customize complex objects with a step-by-step approach.

What is the difference between builder pattern and Factory pattern?

The Builder pattern focuses on constructing a complex object step by step, allowing flexibility. The Factory pattern creates an object in a single step.

What is the builder pattern in API?

In APIs, the Builder pattern simplifies the construction of objects with numerous configuration options, enhancing readability and providing a fluent interface.

Conclusion

The Builder Design Pattern is a powerful tool in a developer's toolkit, offering a structured approach to constructing complex objects. By understanding and implementing this pattern, developers can ensure that their code remains clean, maintainable, and scalable. While it may introduce additional complexity, the benefits of readability and flexibility often outweigh the costs, especially in large-scale applications.

You can refer to our guided paths on the Coding Ninjas. You can check our course to learn more about DSADBMSCompetitive ProgrammingPythonJavaJavaScript, etc. 

Also, check out some of the Guided Paths on topics such as Data Structure and AlgorithmsCompetitive ProgrammingOperating SystemsComputer Networks, DBMSSystem Design, etc., as well as some Contests, Test Series, and Interview Experiences curated by top Industry Experts.

Previous article
Managing Memory in Java
Next article
Is-A Relationship in Java
Live masterclass