Builder Design Pattern in C#

Builder Design Pattern in C# with Examples

In this article, I will discuss the Builder Design Pattern in C# with Examples. Please read our previous article discussing the Abstract Factory Design Pattern in C# with Examples. The Builder Design Pattern falls under the category of the Creational Design Pattern. As part of this article, we will discuss the following pointers.

  1. What is the Builder Design Pattern?
  2. Understanding the Builder Design Pattern with Real-Time Example.
  3. Understanding the class diagram of the Builder Design Pattern?
  4. Implementing the Builder Design Pattern in C#: Generating Different Reports and Beverages
  5. When to use the Builder Design Pattern in Real-time Applications.
What is the Builder Design Pattern?

According to GOF, the Builder Design Pattern builds a complex object using many simple objects and a step-by-step approach. The Process of constructing the complex object should be generic so that the same construction process can be used to create different representations of the same complex object.

The Builder Design Pattern is useful in C# when you need to create an object with many optional and required fields, especially if the object’s construction process is complex or if many representations of the object are possible. The key idea is to separate the construction of a complex object from its representation, allowing the same construction process to create different representations.

So, the Builder Design Pattern is all about separating the construction process from its representation. When the construction process of your object is very complex, only you need to use the Builder Design Pattern. 

Real-Time Example to Understand Builder Design Pattern

Please have a look at the following diagram. Here, the Laptop is a complex object. To build a laptop, we need to use many small objects like LCD Displays, USB Ports, Wireless, Hard Drives, Pointing Devices, Battery, Memory, DVD/CD Reader, Keyboard, Plastic Case, etc. So, we have to assemble all these small objects to build the laptop complex objects.

What is the Builder Design Pattern?

So, to build the complex object laptop, we need to define some generic process, something like below.
1. Plug the memory
2. Plug the Hard Drive
3. Plug the battery
4. Plug the Keyboard
……
……
10. Cover the Laptop with a plastic case

So, using the above process, we can create different types of laptops, such as 14-inch or 17-inch screens. Laptop with 4GB RAM or 8GB RAM. So, like this, we can create different kinds of laptops. So, all the laptop creations will follow the same generic process. So, now, if you read the definition, you will understand the definition of Builder Design Pattern.

So, the Builder Design Pattern separates the construction of a complex object from its representation, allowing the same construction process to create different representations. It is particularly useful for constructing objects with many optional components or configurations.

Example to Understand Builder Design Pattern:

Let us understand the Builder Design Pattern in C# with one Real-Time Example. Suppose we want to develop an application for displaying the reports. The reports we need to display either in Excel or in PDF format. That means we have two types of representation of the reports. To understand this better, please have a look at the following diagram.

Example to Understand Builder Design Pattern

As you can see in the above image, we generate the report in Excel or PDF formats. The construction process involves several steps, such as Creating a new report and setting the report type, header, content, and footer. If you look at the final output, we have one PDF representation and one Excel representation. Please look at the following diagram to understand the construction process and its representation. As you can see, the construction process is the same, but we are getting two types of reports with the same construction process.

Example to Understand Builder Design Pattern in C#

Understanding the UML (or Class) Diagram of Builder Design Pattern in C#

Let us understand the UML or Class Diagram and the different components of the Builder Design Pattern. To understand this, please have a look at the following diagram.

Understanding the UML (or Class) Diagram of Builder Design Pattern

To separate the construction process from its representation, the builder design pattern Involve four components. They are as follows. 

  1. Abstract Builder: The Builder is an interface defining all the steps to make the concrete product. 
  2. Concrete Builder: The Concrete Builder Classes implements the Abstract Builder interface and provides implementation to all the abstract methods. The Concrete Builder is responsible for constructing and assembling the individual parts of the product by implementing the Builder interface. It also defines and tracks the representation it creates.
  3. Director: The Director takes those individual processes from the Builder and defines the sequence to build the product.
  4. Product: The Product is a class, and we want to create this product object using the builder design pattern. This class defines different parts that will make the product.

Note: If you do not understand the above component now, don’t worry. Once we see the example, then we will compare the example with the above UML diagram and compare our example with the above component. I am sure after that, you will understand the above UML Diagram and the different components of the Builder Design Pattern.

Implementation of Builder Design Pattern in C#:

Let us implement the Excel and PDF Report example we discussed using the Builder Design Pattern in C# step by step. 

Step 1: Creating the Product

Create a class file named Report.cs and copy and paste the following code. This is our product class, and within this class, we define the attributes (such as ReportHeader, ReportType, ReportFooter, and ReportContent) that are common to create a report. We also define one method, i.e., DisplayReport, to display the report details in the console.

using System;
namespace BuilderDesignPattern
{
    // It makes sense to use the Builder Design Pattern only 
    // when your products are quite complex 
    // and require extensive configuration.
    // Using the following Report Product class, we can configure different types of Product
    public class Report
    {
        public string ReportType { get; set; }
        public string ReportHeader { get; set; }
        public string ReportFooter { get; set; }
        public string ReportContent { get; set; }

        public void DisplayReport()
        {
            Console.WriteLine("Report Type :" + ReportType);
            Console.WriteLine("Header :" + ReportHeader);
            Console.WriteLine("Content :" + ReportContent);
            Console.WriteLine("Footer :" + ReportFooter);
        }
    }
}

Once we know the definition of the Product we are building, we need to create the Builder.

Step 2: Creating the Abstract Builder Class.

Create a class file named ReportBuilder.cs and then copy and paste the following. This abstract class will provide the blueprint to create different types of products. As it is abstract, it can have the capabilities to have both abstract and non-abstract methods. The subclasses will implement this ReportBuilder abstract class and need to provide implementations for all the abstract methods it contains. The Builder Abstract Class specifies methods for creating the different parts of the Product objects.

namespace BuilderDesignPattern
{
    // The Builder Abstract Class specifies methods for creating the different parts
    // of the Product objects.
    public abstract class ReportBuilder
    {
        protected Report reportObject;

        public abstract void SetReportType();
        public abstract void SetReportHeader();
        public abstract void SetReportContent();
        public abstract void SetReportFooter();

        public void CreateNewReport()
        {
            reportObject = new Report();
        }

        public Report GetReport()
        {
            return reportObject;
        }
    }
}

Notice here that we have four abstract methods. So, each subclass of ReportBuilder will need to implement those four abstract methods to build a report properly. We need to create a few concrete builder classes by implementing the above ReportBuilder interface.

Step 3: Creating Concrete Builder Classes.

The Concrete Builder classes follow the Builder interface and provide specific implementations of the building steps. The Application may have several variations of Builders implemented differently. In our example, we are dealing with two types of reports, i.e., Excel and PDF. So, we need to create two concrete builder classes by implementing the ReportBuilder Abstract Class and providing implementation to the ReportBuilder Abstract Methods.

ExcelReport.cs

Create a class file named ExcelReport.cs and copy and paste the following code. This ExcelReport class implements the ReportBuilder abstract class and the four abstract methods, the blueprint for creating the report objects. This class is used to provide the blueprint for creating the Excel Report. 

namespace BuilderDesignPattern
{
    // The Following Concrete Builder Implementd the ReportBuilder Abstract Class and 
    // provide specific implementations of the steps for Creating ExcelReport. 
    public class ExcelReport : ReportBuilder
    {
        public override void SetReportContent()
        {
            reportObject.ReportContent = "Excel Content Section";
        }

        public override void SetReportFooter()
        {
            reportObject.ReportFooter = "Excel Footer";
        }

        public override void SetReportHeader()
        {
            reportObject.ReportHeader = "Excel Header";
        }

        public override void SetReportType()
        {
            reportObject.ReportType = "Excel";
        }
    }
}
PDFReport.cs

Create a class file named PDFReport.cs and copy and paste the following code. This class also implements the ReportBuilder abstract class and the four abstract methods, the blueprint for creating the report objects. The following PDFReport class is used to create the Report in PDF format.

namespace BuilderDesignPattern
{
    // The Following Concrete Builder Implementd the ReportBuilder Abstract Class and 
    // provide specific implementations of the steps for Creating PDFReport. 
    public class PDFReport : ReportBuilder
    {
        public override void SetReportContent()
        {
            reportObject.ReportContent = "PDF Content Section";
        }

        public override void SetReportFooter()
        {
            reportObject.ReportFooter = "PDF Footer";
        }

        public override void SetReportHeader()
        {
            reportObject.ReportHeader = "PDF Header";
        }

        public override void SetReportType()
        {
            reportObject.ReportType = "PDF";
        }
    }
}

Once we have the required Concrete Builder Classes, we need to create the Director. The Director will execute the required steps in a particular order to create a particular product.

Step 4: Creating the Director

The Director Class in Builder Design Pattern is only responsible for executing the building steps in order. These steps are so generic that these steps will produce different products. It is helpful when producing products according to a specific order or configuration.

Please create a class file named ReportDirector.cs and copy and paste the following code. The following class has one generic method, i.e., MakeReport, which will take the ReportBuilder instance as an input parameter and then create and return a particular report object. See, this class will call the respective methods of the ReportBuilder object (i.e., an object of either ExcelReport or PDFReport ) in a particular order to create a particular format report.

namespace BuilderDesignPattern
{
    // The Director is only responsible for executing the building steps in a particular order. 
    // It is helpful when producing products according to a specific order or configuration. 
    public class ReportDirector
    {
        public Report MakeReport(ReportBuilder reportBuilder)
        {
            reportBuilder.CreateNewReport();
            reportBuilder.SetReportType();
            reportBuilder.SetReportHeader();
            reportBuilder.SetReportContent();
            reportBuilder.SetReportFooter();

            return reportBuilder.GetReport();
        }
    }
}

Note: This MakeReport Method is so generic that it can create and return different report objects. Once we have the Director and Concrete Builder, the Client can use them to create different types of Reports. In our example, the Client is nothing but the Main method of the Program class.

Step 5: Client Code

The client code in the Builder Design Pattern will create a builder object, then pass that builder object to the director, and then the director will initiate the construction process. The end result is nothing but the product that is retrieved from the builder object.

In our example, the Main method of the Program class is going to be the Client. So, please modify the Main Method of the Program class as shown below. Here, first, we will create an instance of the ReportDirector class and then create an instance of the PDFReport class. Once we have the ReportDirector instance and PDFReport instance, we call the MakeReport method on the ReportDirector instance, bypassing the PDFReport instance as an argument that will create and return the report in PDF format. The same process is also for Excel reports.

using System;
namespace BuilderDesignPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            // Constructing the PDF Report
            // Step1: Create a Builder Object 
            // Creating PDFReport Builder Object
            PDFReport pdfReport = new PDFReport();

            // Step2: Pass the Builder Object to the Director
            // First Create an instance of ReportDirector
            ReportDirector reportDirector = new ReportDirector();
            // Then Pass the pdfReport Builder Object to the MakeReport Method of ReportDirector
            // The ReportDirector will return one of the Representations of the Product
            Report report = reportDirector.MakeReport(pdfReport);

            // Step3: Display the Report by calling the DisplayReport method of the Product
            report.DisplayReport();

            Console.WriteLine("-------------------");
            // Constructing the Excel Report
            // The Process is going to be the same
            ExcelReport excelReport = new ExcelReport();
            report = reportDirector.MakeReport(excelReport);
            report.DisplayReport();

            Console.ReadKey();
        }
    }
}

Now run the application, and you should get the output as shown in the image below.

When to use the Builder Design Pattern in Real-Time Applications?

Builder Design Pattern Components in Our Example

Now, let us see the Builder Design Pattern UML Diagram Component with our Example so that you can easily understand the UML Diagram.

Builder Design Pattern UML Diagram

Builder Design Pattern Components in Our Example

  1. Client: Main Method of Program Class
  2. Builder: ReportBuilder.cs
  3. Concrete Builder: ExcelReport.cs and PDFReport.cs
  4. Director: ReportDirector.cs
  5. Product: Report.cs
Another Real-Time Example Builder Design Pattern – Beverage

Let’s say you want to prepare Coffee. To prepare coffee, you must use Water, Milk, Sugar, and Coffee Products. So, what you need to do is mix all these components and prepare coffee, which is shown in the image below. So, Coffee (Right-Hand Side) is the complex object, and the small components (Milk, Water, Sugar, and Coffee products) are the simple objects. So, using these simple objects, you can prepare a complex object, i.e., coffee.

Builder Design Pattern Real-time Example in C#

Suppose you want to prepare Tea, then what you need to do is, you need to use Water, Milk, Sugar, and Tea Products. So, using these small objects (Water, Milk, Sugar, and Tea Products), you can prepare a complex object, i.e., Tea, shown in the image below.

Builder Pattern Real-time Example C#

As we already discussed, the Builder Design Pattern in C# is used to build a complex object using a generic step-by-step process so that the same construction process, i.e., the same steps, can be used to build different representations of the same complex object. If you observe both examples, you will see that the process of preparing Tea and Coffee is the same, only the content is different. So you can create one generic process, something like the one below.

Example of Builder Design Pattern in C#

Using the above generic process, you can now prepare Tea, Coffee, Horlicks, etc. Using the Builder Design Pattern, you can easily implement the above in any programming language. Let us proceed and see how we can implement the above Real-Time Application using the Builder Design Pattern in C#.

Implementation of Builder Design Pattern Real-time Example in C# – Beverage

Let us implement the above real-time example using the Builder Design Pattern in C# step by step.

Step 1: Creating Product (Beverage)

It makes sense to use the Builder Design Pattern only when your products are complex and require extensive configuration. Create a class file named Beverage.cs and copy and paste the following code. This will be our product, and in this class, we put all the attributes such as Water, Milk, Sugar, PowderQuantity, and BeverageName, which are common to prepare beverages (i.e. Coffee or Tea). We also create one method (i.e., ShowBeverage) which returns the beverage details.

namespace BuilderDesignPatternExample
{
    public class Beverage
    {
        public int Water { get; set; }
        public int Milk { get; set; }
        public int Sugar { get; set; }
        public int PowderQuantity { get; set; }
        public string BeverageName { get; set; }
        
        public string ShowBeverage()
        {
            return "Hot " + BeverageName + " [" + Water + " ml of water, " + Milk + "ml of milk, " + Sugar
                            + " gm of sugar, " + PowderQuantity + " gm of " + BeverageName + "]\n";
        }
    }
}
Step 2: Creating Abstract Builder (BeverageBuilder)

The Builder Abstract Class specifies methods for creating the different parts of the Product objects. Create a class file named BeverageBuilder.cs and copy and paste the following. This BeverageBuilder class will be abstract and act as a blueprint for any subclasses wanting to create a Beverage. So, it will have different subclass implementations for different beverage types such as Coffee, Tea, Horlicks, etc. This class has different abstract methods to set the Milk, Water, Sugar, PowderQuantity, and BeverageType.

namespace BuilderDesignPatternExample
{
    public abstract class BeverageBuilder
    {
        protected Beverage beverage;
        
        public void CreateBeverage()
        {
            beverage = new Beverage();
        }
        public Beverage GetBeverage()
        {
            return beverage;
        }
        
        public abstract void SetBeverageType();
        public abstract void SetWater();
        public abstract void SetMilk();
        public abstract void SetSugar();
        public abstract void SetPowderQuantity();
    }
}
Step 3: Creating Concrete Builder (CoffeeBuilder and TeaBuilder)

In our example, we will create two types of beverages, i.e., Tea and Coffee. So, we need to create two concrete builder classes by implementing the BeverageBuilder abstract class and providing implementation to all the abstract methods.

CoffeeBuilder.cs

Create a class file named CoffeeBuilder.cs and copy and paste the following code. The CoffeeBuilder class creates coffee by mixing the components such as water, milk, sugar, and coffee powder. This CoffeeBuilder class implements the BeverageBuilder abstract class, the blueprint for creating the beverage objects.

using System;
namespace BuilderDesignPatternExample
{
    public class CoffeeBuilder : BeverageBuilder
    {
        public override void SetWater()
        {
            Console.WriteLine("Step 1 : Boiling water");
            GetBeverage().Water = 40;
        }

        public override void SetMilk()
        {
            Console.WriteLine("Step 2 : Adding milk");
            GetBeverage().Milk = 50;
        }

        public override void SetSugar()
        {
            Console.WriteLine("Step 3 : Adding Sugar");
            GetBeverage().Sugar = 10;
        }

        public override void SetPowderQuantity()
        {
            Console.WriteLine("Step 4 : Adding 15 Grams of coffee powder");
            GetBeverage().PowderQuantity = 15;
        }

        public override void SetBeverageType()
        {
            Console.WriteLine("Coffee");
            GetBeverage().BeverageName = "Coffee";
        }
    }
}
TeaBuilder.cs

Create a class file named TeaBuilder.cs and copy and paste the following code. This class also implements the BeverageBuilder abstract class and provides implementation to all the abstract methods. The following TeaBuilder class creates Tea by mixing the components such as Water, Milk, Sugar, and Tea powder.

using System;
namespace BuilderDesignPatternExample
{
    public class TeaBuider : BeverageBuilder
    {
        public override void SetWater()
        {
            Console.WriteLine("Step 1 : Boiling water");
            GetBeverage().Water = 50;
        }

        public override void SetMilk()
        {
            Console.WriteLine("Step 2 : Adding milk");
            GetBeverage().Milk = 60;
        }

        public override void SetSugar()
        {
            Console.WriteLine("Step 3 : Adding Sugar");
            GetBeverage().Sugar = 15;
        }

        public override void SetPowderQuantity()
        {
            Console.WriteLine("Step 4 : Adding 20 Grams of coffee powder");
            GetBeverage().PowderQuantity = 20;
        }

        public override void SetBeverageType()
        {
            Console.WriteLine("Tea");
            GetBeverage().BeverageName = "Tea";
        }
    }
}
Step 4: Creating the Director (BeverageDirector)

Once you created the concrete builder classes, then you need to create the director. The Director is only responsible for executing the building steps in order. It is helpful when producing products according to a specific order or configuration.

The director is the component that will execute the required steps in a particular order to create a beverage. So, create a class file named BeverageDirector.cs and copy and paste the following code. This class has one generic method, which will take BeverageBuilder as an input parameter and then create and return a particular beverage object.

namespace BuilderDesignPatternExample
{
    public class BeverageDirector
    {
        public Beverage MakeBeverage(BeverageBuilder beverageBuilder)
        {
            beverageBuilder.CreateBeverage();

            beverageBuilder.SetBeverageType();
            beverageBuilder.SetWater();
            beverageBuilder.SetMilk();
            beverageBuilder.SetSugar();
            beverageBuilder.SetPowderQuantity();
            
            return beverageBuilder.GetBeverage();
        }
    }
}

Note: This MakeBeverage Method is so generic that it can create and return different Beverage objects.

Step 5: Client Code.

Please modify the Main method as shown below. Here, first, we will create an instance of the BeverageDirector class and then create an instance of the TeaBuilder class. Once we have the BeverageDirector and TeaBuilder instances, we call the MakeBeverage method on the BeverageDirector instance, bypassing the TeaBuilder instance as an argument that will create and return the tea. Again, we need to follow the same process to make coffee.

using System;
namespace BuilderDesignPatternExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Beverage beverage;
            BeverageDirector beverageDirector = new BeverageDirector();
            
            TeaBuider tea = new TeaBuider();
            beverage = beverageDirector.MakeBeverage(tea);
            Console.WriteLine(beverage.ShowBeverage());

            CoffeeBuilder coffee = new CoffeeBuilder();
            beverage = beverageDirector.MakeBeverage(coffee);
            Console.WriteLine(beverage.ShowBeverage());

            Console.ReadKey();
        }
    }
}
Output:

Implementation of Builder Design Pattern Real-time Example - Beverage

When to use the Builder Design Pattern in C#?

The Builder Design Pattern in C# is particularly useful in the following scenarios:

  • Complex Object Construction: When an object needs to be created with a complex construction process, especially when this process involves multiple steps or the construction of several sub-objects.
  • Immutable Objects Creation: If you need to create immutable objects (objects that cannot be modified after creation), the Builder pattern allows setting all the required properties at creation time.
  • Constructing Objects with Numerous Parameters: Creating an object involves many parameters, some of which might be optional. The Builder pattern provides a clearer and more readable approach than using multiple constructors (Telescoping Constructor Anti-pattern).
  • Readable Object Creation: Improving the readability and maintainability of code, especially when creating an object involves many parameters that could lead to confusion if a simple constructor is used.
  • Reusable Object Creation Logic: If there’s a need to reuse the same object creation logic in different parts of the application, a Builder can encapsulate this logic in a single place.
  • Fluent Interfaces: When you want to provide a fluent interface for object creation, which guides the client through the creation process step by step, making the client code more readable.
  • Configurable Object Creation: In scenarios where an object needs to be created with different configurations, each requires a different combination of object properties.
  • Variable Construction Process: If the construction process of an object needs to be varied and controlled at runtime, the Builder pattern allows for this flexibility without the need to create numerous different constructors.

In the next article, I will discuss the Builder Design Pattern in Real-time Example in C#. Here, in this article, I try to explain the Builder Design Pattern in C# with Examples. I hope you understand the need and use of the Builder Design Pattern in C#.

2 thoughts on “Builder Design Pattern in C#”

  1. blank

    Very good. Just missed public in below class as below

    class ExcelReport : ReportBuilder
    should be
    public class ExcelReport : ReportBuilder

Leave a Reply

Your email address will not be published. Required fields are marked *