update on   5 min read

Builder Design patterns

Here, I will exaplin about builder design pattern.

Intro

The Builder design pattern is a creational pattern that makes it easier to create complex objects by breaking down the process into simple, manageable steps. It helps developers build objects step by step, which makes the code cleaner and easier to maintain—especially when writing unit tests. Basically, the Builder pattern offers a single class with easy-to-use methods that the client can call. Behind the scenes, it takes care of the complex work, so you don’t have to. This approach separates the object’s construction from how it’s represented, letting both parts change independently without affecting each other. In cloud and data engineering, the Builder pattern is super useful for creating flexible and maintainable systems. It helps you set up your unit test layers smoothly by keeping things organized. To put it simply: the Builder pattern allows you to build complicated objects step by step, making the whole process straightforward and easier to manage.

Core components in Builder Design Pattern

  1. Product -> the complex object being build
  2. Builder -> abstract/ blue print for building objects
  3. Concrete Builder -> implementation of the builder interface to build
  4. Director -> its a optional

Types of builder

  • Simple Builder: Used when the object has a fixed structure but needs a clean way to construct it.
  • Fluent Builder: Uses method chaining for a more readable and expressive syntax.
  • Generic Builder: Uses generics to build different types of objects with reusable logic.
  • Nested Builder: Useful for building hierarchical or nested objects like HTML or UI components.

Which Solid Priciple implemented in this Pattern?

  1. The Pattern primarily implemented Single Responsiablity Prinples (SRP)
    • Each component is implemented to have single responsibility
  2. its encourage the Open and close Principle (OCP).
  • means with interface we can extends it behaviour

UML Diagram

My local photo

Simple Builder

  • Straightforward separation of construction logic.
  • No Method Chaining.
ComponentRoleExample
BuilderDefines stepsIReportBuilder
ConcreteBuilderActual implementation of stepsExcelReportBuilder
ProductThe complex object being createdReport
DirectorControls construction process (optional)ReportDirector
ClientInitiates the build (uses above classes)Your method Main
Example for Simple Builder

In this exaplme , I’m creating reports (either Excel or PDF) with a header, content, and footer. Each report type is built in a slightly different way, but the steps (setting header, content, footer, etc.) are the same.

GitHub Repository

public class Report
{
public string ReportType { get; set; }
public string ReportHeader { get; set; }
public string ReportContent { get; set; }
public string ReportFooter { get; set; }
public void DisplayReport()
{
Console.WriteLine($"Report Type: {ReportType}");
Console.WriteLine($"Header: {ReportHeader}");
Console.WriteLine($"Content: {ReportContent}");
Console.WriteLine($"Footer: {ReportFooter}");
}
}

Builder Interface ()IReportBuilder

  • Defines the steps needed to build a report: , , , and . SetHeader``SetContent``SetFooter``GetReport
public interface IReportBuilder
{
void SetHeader(string header);
void SetContent( string content);
void SetFooter( string footer);
Report GetReport();
}

Concrete Builders PdfReportBuilder

  • Implement these steps to build specific representations (Excel or PDF).
public class ExcelReportBuilder : IReportBuilder
{
private readonly Report _report = new();
public void SetHeader(string header) => _report.ReportHeader = header;
public void SetContent(string content) => _report.ReportContent = content;
public void SetFooter(string footer) => _report.ReportFooter = footer;
public Report GetReport()
{
_report.ReportType = "Excel";
return _report;
}
}

Concrete Builders ExcelReportBuilder

  • Implement these steps to build specific representations (Excel or PDF).
public class PdfReportBuilder : IReportBuilder
{
private readonly Report _report = new();
public void SetHeader(string header) => _report.ReportHeader = header;
public void SetContent(string content) => _report.ReportContent = content;
public void SetFooter(string footer) => _report.ReportFooter = footer;
public Report GetReport()
{
_report.ReportType = "PDF";
return _report;
}
}

Director ()ReportDirector

  • Knows the order in which to call the building steps.
  • Gets a builder and tells it what to build.
public class ReportDirector(IReportBuilder builder)
{
public Report GetReport(string header, string content, string footer)
{
builder.SetHeader(header);
builder.SetContent(content);
builder.SetFooter(footer);
return builder.GetReport();
}
}

Main ()calling without Director

internal static class Program
{
private static void Main(string[] args)
{
IReportBuilder excelBuilder = new ExcelReportBuilder();
excelBuilder.SetHeader("Excel Header");
excelBuilder.SetContent("Excel Content");
excelBuilder.SetFooter("Excel Footer");
excelBuilder.GetReport().DisplayReport();
Console.WriteLine();
IReportBuilder pdfBuilder = new PdfReportBuilder();
pdfBuilder.SetHeader("PDF Header");
pdfBuilder.SetContent("PDF Content");
pdfBuilder.SetFooter("PDF Footer");
pdfBuilder.GetReport().DisplayReport();
}
}

Main ()calling with Director

internal static class Program
{
private static void Main(string[] args)
{
IReportBuilder excelBuilder = new ExcelReportBuilder();
Console.WriteLine("With Director \n" );
var director = new ReportDirector(excelBuilder);
director.GetReport("Excel Header", "Excel Content", "Excel Footer").DisplayReport();
Console.WriteLine();
director = new ReportDirector(pdfBuilder);
director.GetReport("PDF Header", "PDF Content", "PDF Footer").DisplayReport();
}
}

Fluent Builder

The key feature is method chaining, where each method returns the builder object itself (return this), allowing calls to be linked in a single statement.

Fluent Pattern FeatureIn Your Example
Method chainingSetHeader().SetContent().SetFooter()
Enhanced readabilityCode reads like instructions
Builder returns self-referenceEach Set...() returns builder
Final method for result.Build() creates the report
Example for Fluent Builder
  • Each method returns the instance of the builder itself, so you can immediately call the next setup method

GitHub Repository

public class ExcelReportBuilder
{
private readonly Report _report = new();
public ExcelReportBuilder SetHeader(string header)
{
_report.ReportHeader = header;
return this;
}
public ExcelReportBuilder SetContent(string content)
{
_report.ReportContent = content;
return this;
}
public ExcelReportBuilder SetFooter(string footer)
{
_report.ReportFooter = footer;
return this;
}
public Report Build()
{
_report.ReportType = "PDF";
return _report;
}
}
  • Here, I have create a ExcelReportBuilder instance.
  • Methods like .SetHeader(...), .SetContent(...), and .SetFooter(...) are called one after another.
  • .Build() at the end constructs the final report object.
var excelReport = new ExcelReportBuilder()
.SetHeader("Excel Header")
.SetContent("Excel Content")
.SetFooter("Excel Footer")
.Build();
excelReport.DisplayReport();

Generic Builder

The key feature is Reusable logic for building different types.

Example for Generic Builder

I have code that builds a object using a generic builder: Report

GitHub Repository

  1. Instantiating the Builder

    • new GenericBuilder<Report>() creates a new builder for the type. Report
  2. Chaining Set Operations

    • .Set(report => report.Property = value) adds a configuration step for each property:
      • This uses a lambda expression to set the property value.
      • Each call returns the builder itself, enabling method chaining.
public class GenericBuilder<T> where T : new()
{
private T _instance = new();
public GenericBuilder<T> Set(Action<T> setter) {
setter(_instance);
return this;
}
public T Build() => _instance;
}
  1. Building the Object

    • .Build() finally creates the configured instance. Report
  2. Using the Result

    • excalReport.DisplayReport(); makes use of the fully configured report.
internal static class Program
{
private static void Main(string[] args)
{
var excalReport= new GenericBuilder<Report>()
.Set(report => report.ReportType = "Excel")
.Set(report => report.ReportHeader = "Excel Header")
.Set(report => report.ReportContent = "Excel Content")
.Set(report => report.ReportFooter = "Excel Footer")
.Build();
excalReport.DisplayReport();
}
}
Share:
Back to Blog

Related Posts

View All Posts »
Command Design patterns

Command Design patterns

As a part of this article , I will explain about builder design pattern & what problem it solves?