Detailed Explanation of Builder Design Pattern in C#

I am currently writing a series of articles on design patterns, with a focus on the builder design pattern and its benefits in software development. The Builder design pattern, as its name implies, is utilized for constructing intricate structures. It commences with a component, such as a product, which is essential for forming an entire new structure. Let's consider the scenario where you are tasked with constructing a house. In order to build a house step by step, you require fundamental elements like bricks and other materials, and the builder pattern facilitates this process effectively.

Builder design pattern

The Builder design pattern is a creational design pattern that is used to construct a complex object step by step. It separates the construction of a complex object from its representation, allowing the same construction process to create different representations.

When to Use the Builder Design Pattern?

  1. Complex Object Construction: When an object's creation involves a complex process with multiple steps.When there is a need to build an object gradually, especially when configuring it with numerous optional parameters or components.
  2. Varied Representations: When there is a requirement to generate diverse representations of a single object.When the construction algorithm for a complex object should be separate from the object's components and their assembly process.
  3. Unchangeability: When it is necessary to guarantee that the created object remains immutable and that no steps are omitted during the construction process.

Advantages of the Builder Pattern

  1. Precision Object Assembly: Enables the gradual assembly of intricate objects while maintaining control over the entire assembly process. Guarantees consistency throughout the construction process, ensuring all essential steps are executed.
  2. Division of Responsibilities: Decouples the assembly logic from the representation, endorsing the single responsibility principle and enhancing code modularity and maintainability.Enhances adaptability in crafting objects by utilizing the same assembly process to generate different representations.Facilitates effortless modification of a product's internal representation by altering the specific builder.
  3. Enhanced Clarity and Manageability: Enhances the readability and maintenance of code by clearly outlining the steps involved in constructing a complex object.Encloses the assembly and representation logic, simplifying management and understanding.
  4. Encapsulation: Wraps the creation of intricate objects, minimizing the risk of an object being in an inconsistent state.Conceals the assembly process from the client, who only requires interaction with the director and the builder.
  5. Object Construction Control: Enables the gradual construction of intricate objects while maintaining control over the process. Ensures consistency in the construction process and completion of all necessary steps.

Use Cases of Builder Design Pattern
 

Developing Elaborate Objects

The Builder pattern proves to be beneficial when dealing with objects that require a multitude of parameters to be configured, as it streamlines the object creation process by avoiding a cluttered constructor.

Example. Crafting a Mansion object that necessitates setting various components such as rooms, doors, windows, and roofs.

Establishing Immutability

By utilizing Builders, one can create immutable objects by finalizing the object's construction before providing it to the user.

Example. Forming a Profile object with fixed attributes like name, age, address, and so forth.

Simplifying Constructor Complexity

In scenarios where a class boasts multiple constructors with varying parameters, employing a Builder can simplify the object creation process and enhance the code's clarity.

Example. Generating a Vehicle object where diverse specifications (engine type, color, model, etc.) can be specified.

Handling Conditional Construction Logic

The Builder pattern proves to be advantageous when the construction process involves conditional steps, as it enables the encapsulation of this logic within the builder itself.

Example. Developing a Smartphone object where components (CPU, GPU, RAM, etc.) are chosen based on the type of smartphone (gaming, business, etc.).

Enhancing Code Readability

By implementing the Builder pattern, code readability is improved by explicitly outlining the steps involved in creating an object.

Example. Formulating a TravelPackage object where various elements like flights, accommodations, excursions, etc., are arranged in a clear and understandable manner.

Facilitating Multiple Representations

When an object can possess diverse representations, the Builder pattern allows for the creation of these variations without necessitating modifications to the client code.

Example. Generating various types of Invoices (PDF, HTML, JSON) from the same dataset.

Purpose

  • To gradually build a sophisticated object.
  • To distinguish the creation of a complex object from its structure, allowing for the creation of various representations using the same construction process.

Components of Builder Design Pattern

  1. Product (EmployeeDetailsProduct)
    • The product class specifies the structure of the complex object being created. It includes attributes for the employee's information like name, age, job title, department, and skills.
    • The complex object being constructed is represented by ConcreteBuilder. This builder is responsible for creating the internal representation of the product and outlining the assembly process. It consists of classes that specify the individual parts, along with interfaces for putting these parts together to form the end product.
  2. . Builder Interface (IEmployeeDetailsBuilder)
    • This interface outlines all the necessary steps for assembling the product. Each step corresponds to a method for defining a specific aspect of the product (e.g., SetEmployeeName, SetEmployeeAge).
    • It defines an abstract interface for producing components of a Product entity.
  3. Concrete Builders (DeveloperEmployeeConcreteBuilder, ManagerEmployeeConcreteBuilder, TesterEmployeeConcreteBuilder)
    • These classes adhere to the IEmployeeDetailsBuilder interface and offer distinct implementations for each step of the construction process. Every concrete builder constructs and combines components of the product by implementing the interface methods.
    • The product is built by constructing and assembling its parts through the utilization of the Builder interface, maintaining the representation it generates, and offering a means to access the final product.
  4. Director (EmployeeDetailsDirector)
    • The director-class oversees the creation of the product using the builder interface.It is not reliant on the concrete builders and can utilize any builder object that follows the IEmployeeDetailsBuilder interface.
    • The director manages the construction process by specifying the order in which the builder methods are invoked.

A diagram illustrating all components in UML format is provided below.

UML format

Implementation of Builder design pattern
 

Step 1. Product Class (EmployeeDetailsProduct)

  • This class embodies the intricate entity currently under construction.
  • It encompasses attributes for different employee particulars and a function to exhibit them.
    using System;
    using System.Collections.Generic;
    namespace DesignPatterExamples.BuilderPattern
    {
        public class EmployeeDetailsProduct
        {
            public string EmployeeName { get; set; }
            public int EmployeeAge { get; set; }
            public string EmployeePosition { get; set; }
            public string EmployeeDepartment { get; set; }
            public List<string> EmployeeSkills { get; set; } = new List<string>();
            public void DisplayEmployeeInfo()
            {
                Console.WriteLine($"Name: {EmployeeName}");
                Console.WriteLine($"Age: {EmployeeAge}");
                Console.WriteLine($"Position: {EmployeePosition}");
                Console.WriteLine($"Department: {EmployeeDepartment}");
                Console.WriteLine("Skills:");
                foreach (var skill in EmployeeSkills)
                {
                    Console.WriteLine($"- {skill}");
                }
            }
        }
    }
    

Step 2. Builder Interface (IEmployeeDetailsBuilder)

  • Specifies the required methods for implementation by any actual builder.
  • Every method is associated with the configuration of a particular section of the EmployeeDetailsProduct.
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    namespace DesignPatterExamples.BuilderPattern
    {
        public interface IEmployeeDetailsBuilder
        {
            void SetEmployeeName(string name);
            void SetEmployeeAge(int age);
            void SetEmployeePosition(string position);
            void SetEmployeeDepartment(string department);
            void SetEmployeeSkills(List<string> skills);
            EmployeeDetailsProduct GetEmployeeDetails();
        }
    }
    

Step 3. Concrete Builders

The IEmployeeDetailsBuilder interface is implemented by this class, which offers a tailored implementation for constructing the employee details of a developer.This class implements the IEmployeeDetailsBuilder interface and provides specific implementation for building a developer's employee details.

DeveloperEmployeeConcreteBuilder.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DesignPatterExamples.BuilderPattern
{
    public class DeveloperEmployeeConcreteBuilder : IEmployeeDetailsBuilder
    {
        private EmployeeDetailsProduct employeeDetailsProduct = new EmployeeDetailsProduct();
        public EmployeeDetailsProduct GetEmployeeDetails()
        {
            return employeeDetailsProduct;
        }
        public void SetEmployeeAge(int age)
        {
            employeeDetailsProduct.EmployeeAge = age;
        }
        public void SetEmployeeDepartment(string department)
        {
            employeeDetailsProduct.EmployeeDepartment = department;
        }
        public void SetEmployeeName(string name)
        {
            employeeDetailsProduct.EmployeeName = name;
        }
        public void SetEmployeePosition(string position)
        {
            employeeDetailsProduct.EmployeePosition = position;
        }
        public void SetEmployeeSkills(List<string> skills)
        {
            employeeDetailsProduct.EmployeeSkills = skills;
        }
    }
}

This class implements the IEmployeeDetailsBuilder interface and provides a specific implementation for building a manager's employee details.

ManagerEmployeeConcreteBuilder.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DesignPatterExamples.BuilderPattern
{
    public class ManagerEmployeeConcreteBuilder : IEmployeeDetailsBuilder
    {
        private EmployeeDetailsProduct employeeDetailsProduct = new EmployeeDetailsProduct();
        public EmployeeDetailsProduct GetEmployeeDetails()
        {
            return employeeDetailsProduct;
        }
        public void SetEmployeeAge(int age)
        {
            employeeDetailsProduct.EmployeeAge = age;
        }
        public void SetEmployeeDepartment(string department)
        {
            employeeDetailsProduct.EmployeeDepartment = department;
        }
        public void SetEmployeeName(string name)
        {
            employeeDetailsProduct.EmployeeName = name;
        }
        public void SetEmployeePosition(string position)
        {
            employeeDetailsProduct.EmployeePosition = position;
        }
        public void SetEmployeeSkills(List<string> skills)
        {
            employeeDetailsProduct.EmployeeSkills = skills;
        }
    }
}

This class implements the IEmployeeDetailsBuilder interface and provides a specific implementation for building a tester's employee details.

TesterEmployeeConcreteBuilder.cs

using System;
using System.Collections.Generic;
namespace DesignPatterExamples.BuilderPattern
{
    public class TesterEmployeeConcreteBuilder : IEmployeeDetailsBuilder
    {
        private EmployeeDetailsProduct employeeDetailsProduct = new EmployeeDetailsProduct();
        public EmployeeDetailsProduct GetEmployeeDetails()
        {
            return employeeDetailsProduct;
        }
        public void SetEmployeeAge(int age)
        {
            employeeDetailsProduct.EmployeeAge = age;
        }
        public void SetEmployeeDepartment(string department)
        {
            employeeDetailsProduct.EmployeeDepartment = department;
        }
        public void SetEmployeeName(string name)
        {
            employeeDetailsProduct.EmployeeName = name;
        }
        public void SetEmployeePosition(string position)
        {
            employeeDetailsProduct.EmployeePosition = position;
        }
        public void SetEmployeeSkills(List<string> skills)
        {
            employeeDetailsProduct.EmployeeSkills = skills;
        }
    }
}

Step 4. Director Class (EmployeeDetailsDirector)

  • The director class controls the building process.
  • It uses the builder interface to construct an EmployeeDetailsProduct by specifying the sequence in which the builder methods are called.
    using System;
    using System.Collections.Generic;
    namespace DesignPatterExamples.BuilderPattern
    {
        public class EmployeeDetailsDirector
        {
            private IEmployeeDetailsBuilder employeeDetailsBuilder;
            public void SetBuilder(IEmployeeDetailsBuilder builder)
            {
                employeeDetailsBuilder = builder;
            }
    
            public void BuildEmployee(
                string employeeName,
                int employeeAge,
                string employeePosition,
                string employeeDepartment,
                List<string> employeeSkills)
            {
                employeeDetailsBuilder.SetEmployeeName(employeeName);
                employeeDetailsBuilder.SetEmployeeAge(employeeAge);
                employeeDetailsBuilder.SetEmployeePosition(employeePosition);
                employeeDetailsBuilder.SetEmployeeDepartment(employeeDepartment);
                employeeDetailsBuilder.SetEmployeeSkills(employeeSkills);
            }
            public EmployeeDetailsProduct GetEmployee()
            {
                return employeeDetailsBuilder.GetEmployeeDetails();
            }
        }
    }
    

Step 5. Director class implementation

List<string> developerSkills = new List<string>();
developerSkills.Add("C#");
developerSkills.Add("Javascript");
developerSkills.Add("MAUI");

List<string> testerSkills = new List<string>();
testerSkills.Add("Manual Testing");
testerSkills.Add("Automated Testing");

List<string> managerSkills = new List<string>();
managerSkills.Add("Project Management");
managerSkills.Add("Team Handling");

EmployeeDetailsDirector employeeDetailsDirector = new EmployeeDetailsDirector();

// Build Developer related information
IEmployeeDetailsBuilder developerBuilder = new DeveloperEmployeeConcreteBuilder();
employeeDetailsDirector.SetBuilder(developerBuilder);
employeeDetailsDirector.BuildEmployee(
    employeeName: "Amit Kumar",
    employeeAge: 30,
    employeePosition: "Senior Developer",
    employeeDepartment: "IT",
    employeeSkills: developerSkills
);
EmployeeDetailsProduct developer = employeeDetailsDirector.GetEmployee();
Console.WriteLine("Developer Information:");
developer.DisplayEmployeeInfo();
Console.WriteLine();
// Build Tester related information
IEmployeeDetailsBuilder testerBuilder = new TesterEmployeeConcreteBuilder();
employeeDetailsDirector.SetBuilder(testerBuilder);
employeeDetailsDirector.BuildEmployee(
    employeeName: "Aman Kumar",
    employeeAge: 30,
    employeePosition: "Senior Tester",
    employeeDepartment: "IT",
    employeeSkills: testerSkills
);
EmployeeDetailsProduct tester = employeeDetailsDirector.GetEmployee();
Console.WriteLine("Tester Information:");
tester.DisplayEmployeeInfo();
Console.WriteLine();
// Build Manager related information
IEmployeeDetailsBuilder managerBuilder = new ManagerEmployeeConcreteBuilder();
employeeDetailsDirector.SetBuilder(managerBuilder);
employeeDetailsDirector.BuildEmployee(
    employeeName: "Sanjay Kumar",
    employeeAge: 30,
    employeePosition: "Product Manager",
    employeeDepartment: "IT",
    employeeSkills: managerSkills
);
EmployeeDetailsProduct manager = employeeDetailsDirector.GetEmployee();
Console.WriteLine("Manager Information:");
manager.DisplayEmployeeInfo();

Repository Path: https://github.com/OmatrixTech/DesignPatterExamples

To be continued…..


Similar Articles