Design patterns are proven solutions to software design problems. They help improve code quality, promote reusability, and increase maintainability. We use them to save time and produce quality, extensible and flexible code. In this article, we are going to introduce the Builder design pattern.
The Builder pattern is a creational pattern that is used to separate the process of building a complex object from that class itself. In this way, to create an object, we must use a separate class that is responsible for creating that object. Builder class can have different steps that must be called before building the object and set different properties of the object.
The builder pattern is especially useful when building complex objects that have multiple configuration modes, such as setting default values, adding optional values, and customizing the object for specific needs. Using this pattern, the construction process can be separated from the object itself, allowing for greater flexibility and maintainability.

The main elements in the builder pattern are:
Builder: An interface that specifies the steps to build an object.
Concrete Builder: A class that implements the Builder interface and defines its methods as needed.
Director: A class that controls the construction process and calls the appropriate Concrete Builder methods to build the final object.
Product: The final object that is made by the Builder.
Below is an example of a builder pattern in C# language:
public interface IBuilder
{
void SetOptionA(string optionA);
void SetOptionB(string optionB);
void SetOptionC(string optionC);
Product GetResult();
}
public class ConcreteBuilder : IBuilder
{
private Product _product = new Product();
public void SetOptionA(string optionA)
{
_product.OptionA = optionA;
}
public void SetOptionB(string optionB)
{
_product.OptionB = optionB;
}
public void SetOptionC(string optionC)
{
_product.OptionC = optionC;
}
public Product GetResult()
{
return _product;
}
}
public class Director
{
private readonly IBuilder _builder;
public Director(IBuilder builder)
{
_builder = builder;
}
public void Construct()
{
_builder.SetOptionA("Option A");
_builder.SetOptionB("Option B");
_builder.SetOptionC("Option C");
}
}
public class Product
{
public string OptionA { get; set; }
public string OptionB { get; set; }
public string OptionC { get; set; }
}In this example, the IBuilder interface defines the steps to build the object, and the ConcreteBuilder class implements the IBuilder interface and completes each step as needed. The Director class controls the construction process and calls the appropriate ConcreteBuilder methods to construct the final object. Finally, the Product class represents the final object created by the Builder.
Below is an example of using these classes in the program:
var builder = new ConcreteBuilder();
var director = new Director(builder);
director.Construct();
var product = builder.GetResult();A real example:
A real example of the builder pattern is the process of building a computer and customizing it with different hardware components.
In the construction of a computer, there are various hardware components such as CPU, GPU, RAM, storage devices, etc. Each of these components has various settings and options that can be customized, such as speed, capacity, etc.
To use the builder pattern in building a computer, you can define a builder interface that includes methods for adding each part to the computer, their settings, and creating the final computer object.
public interface IBuilder
{
void AddCPU(string model, int cores, double clockSpeed);
void AddGPU(string model, int vram, double clockSpeed);
void AddRAM(string model, int capacity, int speed);
void AddStorage(string model, int capacity, string type);
void SetPowerSupply(int wattage);
PC GetResult();
}We can then create a ConcreteBuilder class to implement the IBuilder interface and complete its methods.
public class ConcreteBuilder : IBuilder
{
private PC _pc = new PC();
public void AddCPU(string model, int cores, double clockSpeed)
{
_pc.CPU = new CPU(model, cores, clockSpeed);
}
public void AddGPU(string model, int vram, double clockSpeed)
{
_pc.GPU = new GPU(model, vram, clockSpeed);
}
public void AddRAM(string model, int capacity, int speed)
{
_pc.RAM = new RAM(model, capacity, speed);
}
public void AddStorage(string model, int capacity, string type)
{
_pc.Storage = new Storage(model, capacity, type);
}
public void SetPowerSupply(int wattage)
{
_pc.PowerSupply = new PowerSupply(wattage);
}
public PC GetResult()
{
return _pc;
}
}Finally, a Director class can be created to control the build process and call the ConcreteBuilder methods needed to build the final PC object.
public class Director
{
private readonly IBuilder _builder;
public Director(IBuilder builder)
{
_builder = builder;
}
public void Construct()
{
_builder.AddCPU("Intel Core i7", 8, 3.6);
_builder.AddGPU("NVIDIA GeForce RTX 3070", 8, 1.5);
_builder.AddRAM("Corsair Vengeance LPX", 16, 3200);
_builder.AddStorage("Samsung 970 EVO", 1, "NVMe");
_builder.SetPowerSupply(650);
}
}To use the pattern created in the computer build, we must first create an instance of ConcreteBuilder. Next, we create an instance of Director and pass ConcreteBuilder as a parameter. Finally, we call the Construct method to create the final PC object.
var builder = new ConcreteBuilder();
var director = new Director(builder);
director.Construct();
var pc = builder.GetResult();This pattern has been used in many frameworks, knowing behind the scenes will help us to use these frameworks correctly.
To read about other design patterns, you can use the list below. There is also a code repository on GitHub that includes all the design patterns.
I am Reza Babakhani, a software developer. Here I write my experiences, opinions and suggestions about technology. I hope that what I write is useful for you.
leave a comment