Blog / Design Patterns
The Builder Design Pattern in C# and .Net
  • Dec 08, 2020
  • 69
  • 75

The first pattern in Creational Design Patterns is Builder which allows you to construct complex objects piecewise. Let's get to know more about this design pattern.

Note: Code can be downloaded at my Github


Advertisement
 


1. What is the Builder Pattern?

The Builder Design Pattern helps to separate the construction from the representation of a complex object. Therefore, by utilizing the same construction process, the program can create different representations.

Builder is a creational design pattern that lets you construct complex object step by step. The pattern allows you to produce different types and representations of an object using the same construction code.


2. When to Implement the Builder Pattern?

As the definition suggests, when you need to create a complex object which has different representations, you would need to implement a solid Builder Design Pattern.

Let's take an example of building HTML string without implementing Builder pattern. Create a Console Application in Visual Studio and name it BuilderDemo. In Program.cs, we have below code.

class Program
{
    static void Main(string[] args)
    {
        var hello = "hello";
        var sb = new StringBuilder();
        sb.Append("<p>");
        sb.Append(hello);
        sb.Append("</p>");
        Console.WriteLine(sb);
        sb.Clear();
 
        var words = new[] { "hello", "world" };
        sb.Append("<ul>");
        foreach(var word in words)
        {
            sb.AppendFormat("<li>{0}</li>", word);
        }
        sb.Append("</ul>");
        Console.WriteLine(sb);
    }
}

The code looks fine and is able to produce the HTML strings that we need. However, for each representation, the program requires different creational implementations to construct the results. Here, we can see more works need to be done for the unordered list element (<ul></ul>) than the paragraph element (<p></p>).


3. Example of the Builder Design Pattern

HTML is a complex object and it has different representations. So, we need to implement a central mechanism which is able to produce different HTML elements. That central mechanism where we implement the Builder Design Pattern.

Step 1: Create an HTMLElement class

This class is for initiating html element object which hold the information for each element and its child elements.

public class HTMLElement
{
    public string Name { get; set; }
 
    public string Text { get; set; }
 
    public List<HTMLElement> Elements = new List<HTMLElement>();
}

Step 2: Create an HTMLBuilder class

This class is a builder which is used to construct the HTML string by defining and adding elements.

public class HTMLBuilder
{
    private const int indentSize = 2;
    private HTMLElement rootElement;
 
    public HTMLBuilder(string rootName)
    {
        rootElement = new HTMLElement();
        rootElement.Name = rootName;
    }
 
    public HTMLBuilder AddChild(string name, string text)
    {
        rootElement.Elements.Add(new HTMLElement()
        {
            Name = name,
            Text = text
        });
 
        return this;
    }
 
    public string ToStringImpl(HTMLElement root, int indent)
    {
        var sb = new StringBuilder();
        var i = new string(' ', indentSize * indent);
        if(!string.IsNullOrEmpty(root.Name))
            sb.AppendLine($"{i}<{root.Name}>");
 
        if (!string.IsNullOrEmpty(root.Text))
        {
            sb.Append(new string(' ', indentSize * (indent + 1)));
            sb.AppendLine(root.Text);
        }
 
        foreach (var e in root.Elements)
        {
            sb.Append(ToStringImpl(e, string.IsNullOrEmpty(root.Name) ? indent : indent + 1));
        }
 
        if (!string.IsNullOrEmpty(root.Name))
            sb.AppendLine($"{i}</{root.Name}>");
        return sb.ToString();
    }
 
    public override string ToString()
    {
        return ToStringImpl(rootElement, 0);
    }
}

So from now, if you want to create an HTML string, just need to initiate the builder and add elements by calling AddChild function. It's the same way of create the 2 objects but result in 2 different presentations.

class Program
{
    static void Main(string[] args)
    {
        var pBuilder = new HTMLBuilder("");
        pBuilder.AddChild("p", "hello world");
        Console.WriteLine(pBuilder.ToString());
 
        var ulBuilder = new HTMLBuilder("ul");
        ulBuilder.AddChild("li", "hello").AddChild("li", "world");
        Console.WriteLine(ulBuilder.ToString());
    }
}


Advertisement
 


4. Functional Builder

We can enhance the Builder Pattern to be better in a more functional way. If you read my post about The S.O.L.I.D Design Principles, you will see the Functional Builder implementation follows the Open-Closed principle which is open for extension but closed for modification.

Let's take a look at an example of a PersonBuilder below.

public class Person
{
    public string Name, Position;
}
 
public abstract class FunctionalBuilder<TSubject, TSelf>
    where TSelf : FunctionalBuilder<TSubject, TSelf>
    where TSubject: new()
{
    private readonly List<Func<TSubject, TSubject>> actions
        = new List<Func<TSubject, TSubject>>();
 
    public TSelf Do(Action<TSubject> action)
        => AddAction(action);
 
    public TSubject Build()
        => actions.Aggregate(new TSubject(), (p, f) => f(p));
 
    private TSelf AddAction(Action<TSubject> action)
    {
        actions.Add(p =>
        {
            action(p);
            return p;
        });
        return (TSelf)this;
    }
}
 
public sealed class PersonBuilder
    : FunctionalBuilder<Person, PersonBuilder>
{
    public PersonBuilder Called(string name)
        => Do(p => p.Name = name);
}
 
public static class PersonBuilderExtensions
{
    public static PersonBuilder WorkAs
        (this PersonBuilder builder, string position)
        => builder.Do(p => p.Position = position);
}

Then, in the main function, you can use the Functional Builder implemented above to initiate a Person object:

class Program
{
    static void Main(string[] args)
    {
        var person = new PersonBuilder()
            .Called("Lucas")
            .WorkAs("Developer")
            .Build();
    }
}


5. Facade Builder

Sometimes, using a single builder to build up a particular object is not enough. In many cases, you want several builders which are responsible for building up several different aspects of an object. This is when a Facade Builder Design Pattern comes in the place.

Take a look at below example of the Facade Builder for Employee class.

public class Employee
{
    public string StreetAddress, PostalCode, City;
 
    public string CompanyName, Position;
    public int AnnualIncome;
 
    public override string ToString()
    {
        return base.ToString();
    }
}
 
public class EmployeeBuilder //facade
{
    //reference
    protected Employee emp = new Employee();
 
    public EmployeeAddressBuilder Lives => new EmployeeAddressBuilder(emp);
    public EmployeeJobBuilder Works => new EmployeeJobBuilder(emp);
}
 
public class EmployeeAddressBuilder : EmployeeBuilder
{
    public EmployeeAddressBuilder(Employee emp)
    {
        this.emp = emp;
    }
 
    public EmployeeAddressBuilder At(string streetAddress)
    {
        emp.StreetAddress = streetAddress;
        return this;
    }
 
    public EmployeeAddressBuilder In(string city)
    {
        emp.City = city;
        return this;
    }
 
    public EmployeeAddressBuilder WithPostalCode(string postalCode)
    {
        emp.PostalCode = postalCode;
        return this;
    }
}
 
public class EmployeeJobBuilder : EmployeeBuilder
{
    public EmployeeJobBuilder(Employee emp)
    {
        this.emp = emp;
    }
 
    public EmployeeJobBuilder At(string companyName)
    {
        emp.CompanyName = companyName;
        return this;
    }
 
    public EmployeeJobBuilder AsA(string position)
    {
        emp.Position = position;
        return this;
    }
 
    public EmployeeJobBuilder Earning(int amount)
    {
        emp.AnnualIncome = amount;
        return this;
    }
}

In Main function, we can initiate an employee object using the Faceted Builder implemented above.

class Program
{
    static void Main(string[] args)
    {
        var employee = new EmployeeBuilder()
            .Lives.At("Avenue O")
                    .In("Brooklyn")
                    .WithPostalCode("11333")
            .Works.At("Lucasology")
                    .AsA("Developer")
                    .Earning(100000);
    }
}

We now have a basic idea of implementing Builder Design Pattern from the example above. Let me know your thoughts on this topic and if you have any suggestions or questions, please comment in below this post.


If you have a Website or a Web API developed by using .Net Core and looking for a way to publish your applications, this post will explain how to do it using GoDaddy Windows Hosting.Note: at this mome ...

Search text in Stored Procedure in SQL SELECT DISTINCT o.name AS Object_Name, o.type_desc FROM sys.sql_modules m INNER JOIN sys.objects o ON m.object_id = o ...

Using cherry-pick to select specific commits for your Pull Request.1. Create a new branch based on the target of the Pull Requestgit branch cherry-branch origin/master2. Switch to a new branchgit chec ...

After deployment Angular and API on IIS, it's working fine unless I refresh the page. Once refreshed, the web encountered 404 error. In this article, I will explain how to resolve this.Since Angular i ...

There are some benefits of keeping both UI and API parts in the same place for small projects. In this article, I will explain how I did to deploy Angular Web and ASP .Net Core API in the same folder ...

I got CORS error after publishing my API and Angular app to IIS even though CORS is enabled and the origins of the Angular app is added. Below is how I resolved this issue.Just simple, make sure you s ...

1. The Situation:Error Message:&nbsp;Pulse Secure Application failed to load Java. Please install correct JRE version.Description: This issue happens when I'm using a M1 Mac with a correct version of ...

Accelerated Mobile Pages (AMP)&nbsp;focuses on delivering static content from publishers as quickly as possible and possibly rewards early adopters with a boost in rank. Let's see how to implement it ...

Below is how to decrypt/convert a Hex string value into text using VB.Net:Decrypting Hex string value to string in VB.Net Function HexToString(ByVal hex As String) As String Dim text As New Sy ...