Builder Design Pattern: Comprehensive Explanation

The Builder pattern is a creational design pattern that separates the construction of a complex object from its representation. It allows the same construction process to create different representations of an object.


Core Intent

“Separate the construction of a complex object from its representation so that the same construction process can create different representations.”

This enables step-by-step construction of objects while keeping the final product flexible and reusable.


When to Use Builder

ScenarioUse Case
Object has many optional parametersAvoid telescoping constructors
Construction involves multiple stepsEnsure valid intermediate states
Need different representations of same datae.g., HTML vs JSON vs XML
Construction logic is complex or conditionalEncapsulate in steps
Want immutable final objectsBuild then seal

Avoid when:

  • Object is simple → Direct constructor suffices
  • Performance is critical → Slight overhead

Where It Is Used in Real Systems

DomainExample
UI RenderingGenerating HTML, PDF, or plain text reports
Document GenerationWord, PDF, Markdown exporters
Query BuildingSQL SELECT … WHERE … ORDER BY
Configuration ObjectsHttpClient with headers, timeout, auth
Game Entity CreationCharacter with stats, inventory, skills

Key Benefits

  • Step-by-step construction
  • Reusability of construction logic
  • Immutability of final product
  • Readability via fluent API
  • Validation at build time

The Builder pattern is a creational design pattern that separates the construction of a complex object from its representation. It allows the same construction process to create different representations of an object.


Core Intent

“Separate the construction of a complex object from its representation so that the same construction process can create different representations.”

This enables step-by-step construction of objects while keeping the final product flexible and reusable.


When to Use Builder

ScenarioUse Case
Object has many optional parametersAvoid telescoping constructors
Construction involves multiple stepsEnsure valid intermediate states
Need different representations of same datae.g., HTML vs JSON vs XML
Construction logic is complex or conditionalEncapsulate in steps
Want immutable final objectsBuild then seal

Avoid when:

  • Object is simple → Direct constructor suffices
  • Performance is critical → Slight overhead

Where It Is Used in Real Systems

DomainExample
UI RenderingGenerating HTML, PDF, or plain text reports
Document GenerationWord, PDF, Markdown exporters
Query BuildingSQL SELECT … WHERE … ORDER BY
Configuration ObjectsHttpClient with headers, timeout, auth
Game Entity CreationCharacter with stats, inventory, skills

Key Benefits

  • Step-by-step construction
  • Reusability of construction logic
  • Immutability of final product
  • Readability via fluent API
  • Validation at build time

Real-World Example: E-Commerce Order Receipt Generator

An e-commerce platform generates order receipts in multiple formats:

  • HTML (for email)
  • Plain Text (for SMS/console)
  • PDF (for downloadable invoice)

Each receipt includes:

  • Header (store name, date)
  • Customer info
  • Order items (with price, qty, subtotal)
  • Tax & shipping
  • Total
  • Footer (thank you, terms)

We want:

  • Same data → different output formats
  • Clean, readable construction
  • Extensible to new formats (JSON, XML, etc.)

C# Implementation

using System;
using System.Collections.Generic;
using-System.Text;

// ==================== PRODUCT ====================

public class OrderReceipt
{
    public string StoreName { get; private set; }
    public DateTime OrderDate { get; private set; }
    public string CustomerName { get; private set; }
    public string CustomerEmail { get; private set; }
    public List<OrderItem> Items { get; private set; } = new();
    public decimal Tax { get; private set; }
    public decimal Shipping { get; private set; }
    public decimal Total => Items.Sum(i => i.Subtotal) + Tax + Shipping;
    public string FooterNote { get; private set; }

    // Internal constructor — only Builder can create
    internal OrderReceipt() { }

    public class OrderItem
    {
        public string Name { get; set; }
        public int Quantity { get; set; }
        public decimal UnitPrice { get; set; }
        public decimal Subtotal => Quantity * UnitPrice;
    }

    // For debugging / display
    public override string ToString()
        => $"Receipt[Items={Items.Count}, Total={Total:C}]";
}

// ==================== BUILDER INTERFACE ====================

public interface IReceiptBuilder
{
    IReceiptBuilder SetStoreInfo(string name, DateTime date);
    IReceiptBuilder SetCustomer(string name, string email);
    IReceiptBuilder AddItem(string name, int qty, decimal price);
    IReceiptBuilder SetTax(decimal tax);
    IReceiptBuilder SetShipping(decimal shipping);
    IReceiptBuilder SetFooter(string note);
    OrderReceipt Build();
    string GetResult(); // Final formatted string
}

// ==================== CONCRETE BUILDERS ====================

public class HtmlReceiptBuilder : IReceiptBuilder
{
    private readonly OrderReceipt _receipt = new();
    private readonly StringBuilder _html = new();

    public IReceiptBuilder SetStoreInfo(string name, DateTime date)
    {
        _receipt.StoreName = name;
        _receipt.OrderDate = date;
        return this;
    }

    public IReceiptBuilder SetCustomer(string name, string email)
    {
        _receipt.CustomerName = name;
        _receipt.CustomerEmail = email;
        return this;
    }

    public IReceiptBuilder AddItem(string name, int qty, decimal price)
    {
        _receipt.Items.Add(new OrderReceipt.OrderItem
        {
            Name = name,
            Quantity = qty,
            UnitPrice = price
        });
        return this;
    }

    public IReceiptBuilder SetTax(decimal tax)
    {
        _receipt.Tax = tax;
        return this;
    }

    public IReceiptBuilder SetShipping(decimal shipping)
    {
        _receipt.Shipping = shipping;
        return this;
    }

    public IReceiptBuilder SetFooter(string note)
    {
        _receipt.FooterNote = note;
        return this;
    }

    public OrderReceipt Build()
    {
        GenerateHtml();
        return _receipt;
    }

    public string GetResult() => _html.ToString();

    private void GenerateHtml()
    {
        _html.AppendLine("<!DOCTYPE html><html><body>");
        _html.AppendLine($"<h1>{_receipt.StoreName}</h1>");
        _html.AppendLine($"<p>Date: {_receipt.OrderDate:yyyy-MM-dd}</p>");
        _html.AppendLine($"<p>Customer: {_receipt.CustomerName} ({_receipt.CustomerEmail})</p>");
        _html.AppendLine("<table border='1'><tr><th>Item</th><th>Qty</th><th>Price</th><th>Total</th></tr>");

        foreach (var item in _receipt.Items)
        {
            _html.AppendLine($"<tr><td>{item.Name}</td><td>{item.Quantity}</td><td>{item.UnitPrice:C}</td><td>{item.Subtotal:C}</td></tr>");
        }

        _html.AppendLine("</table>");
        _html.AppendLine($"<p>Tax: {_receipt.Tax:C}</p>");
        _html.AppendLine($"<p>Shipping: {_receipt.Shipping:C}</p>");
        _html.AppendLine($"<p><strong>Total: {_receipt.Total:C}</strong></p>");
        _html.AppendLine($"<footer>{_receipt.FooterNote}</footer>");
        _html.AppendLine("</body></html>");
    }
}

public class TextReceiptBuilder : IReceiptBuilder
{
    private readonly OrderReceipt _receipt = new();
    private readonly StringBuilder _text = new();

    public IReceiptBuilder SetStoreInfo(string name, DateTime date)
    {
        _receipt.StoreName = name;
        _receipt.OrderDate = date;
        return this;
    }

    public IReceiptBuilder SetCustomer(string name, string email)
    {
        _receipt.CustomerName = name;
        _receipt.CustomerEmail = email;
        return this;
    }

   

    public IReceiptBuilder AddItem(string name, int qty, decimal price)
    {
        _receipt.Items.Add(new OrderReceipt.OrderItem
        {
            Name = name,
            Quantity = qty,
            UnitPrice = price
        });
        return this;
    }

    public IReceiptBuilder SetTax(decimal tax)
    {
        _receipt.Tax = tax;
        return this;
    }

    public IReceiptBuilder SetShipping(decimal shipping)
    {
        _receipt.Shipping = shipping;
        return this;
    }

    public IReceiptBuilder SetFooter(string note)
    {
        _receipt.FooterNote = note;
        return this;
    }

    public OrderReceipt Build()
    {
        GenerateText();
        return _receipt;
    }

    public string GetResult() => _text.ToString();

    private void GenerateText()
    {
        _text.AppendLine($"{_receipt.StoreName}");
        _text.AppendLine($"Date: {_receipt.OrderDate:yyyy-MM-dd}");
        _text.AppendLine($"Customer: {_receipt.CustomerName} <{_receipt.CustomerEmail}>");
        _text.AppendLine(new string('-', 50));

        foreach (var item in _receipt.Items)
        {
            _text.AppendLine($"{item.Name.PadRight(25)} {item.Quantity,3} x {item.UnitPrice,8:C} = {item.Subtotal,10:C}");
        }

        _text.AppendLine(new string('-', 50));
        _text.AppendLine($"Tax:      {_receipt.Tax,38:C}");
        _text.AppendLine($"Shipping: {_receipt.Shipping,38:C}");
        _text.AppendLine($"TOTAL:    {_receipt.Total,38:C}");
        _text.AppendLine(_receipt.FooterNote);
    }
}

// Optional: PDF Builder (simplified)
public class PdfReceiptBuilder : IReceiptBuilder
{
    private readonly OrderReceipt _receipt = new();
    private string _pdfPath;

    // Same fluent methods...
    public IReceiptBuilder SetStoreInfo(string name, DateTime date) { _receipt.StoreName = name; _receipt.OrderDate = date; return this; }
    public IReceiptBuilder SetCustomer(string name, string email) { _receipt.CustomerName = name; _receipt.CustomerEmail = email; return this; }
    public IReceiptBuilder AddItem(string name, int qty, decimal price) { _receipt.Items.Add(new OrderReceipt.OrderItem { Name = name, Quantity = qty, UnitPrice = price }); return this; }
    public IReceiptBuilder SetTax(decimal tax) { _receipt.Tax = tax; return this; }
    public IReceiptBuilder SetShipping(decimal shipping) { _receipt.Shipping = shipping; return this; }
    public IReceiptBuilder SetFooter(string note) { _receipt.FooterNote = note; return this; }

    public OrderReceipt Build()
    {
        GeneratePdf();
        return _receipt;
    }

    public string GetResult() => _pdfPath;

    private void GeneratePdf()
    {
        _pdfPath = $"receipt_{Guid.NewGuid():N}.pdf";
        Console.WriteLine($"[PDF] Generated: {_pdfPath} (simulated)");
    }
}

// ==================== DIRECTOR ====================

public class ReceiptDirector
{
    public void ConstructStandardReceipt(IReceiptBuilder builder, OrderData data)
    {
        builder
            .SetStoreInfo("GlobalMart", data.OrderDate)
            .SetCustomer(data.CustomerName, data.CustomerEmail)
            .SetTax(data.Tax)
            .SetShipping(data.Shipping)
            .SetFooter("Thank you for shopping with us!");

        foreach (var item in data.Items)
        {
            builder.AddItem(item.name, item.quantity, item.price);
        }
    }
}

public class OrderData
{
    public DateTime OrderDate { get; set; }
    public string CustomerName { get; set; }
    public string CustomerEmail { get; set; }
    public decimal Tax { get; set; }
    public decimal Shipping { get; set; }
    public List<(string name, int quantity, decimal price)> Items { get; set; } = new();
}

// ==================== CLIENT / DEMO ====================

class Program
{
    static void Main()
    {
        var order = new OrderData
        {
            OrderDate = DateTime.Now,
            CustomerName = "Alice Johnson",
            CustomerEmail = "alice@example.com",
            Tax = 12.50m,
            Shipping = 5.99m,
            Items = new List<(string, int, decimal)>
            {
                ("Laptop", 1, 999.00m),
                ("Mouse", 2, 25.00m),
                ("Keyboard", 1, 75.00m)
            }
        };

        var director = new ReceiptDirector();

        Console.WriteLine("=== HTML RECEIPT ===");
        var htmlBuilder = new HtmlReceiptBuilder();
        director.ConstructStandardReceipt(htmlBuilder, order);
        Console.WriteLine(htmlBuilder.GetResult());

        Console.WriteLine("\n=== TEXT RECEIPT ===");
        var textBuilder = new TextReceiptBuilder();
        director.ConstructStandardReceipt(textBuilder, order);
        Console.WriteLine(textBuilder.GetResult());

        Console.WriteLine("=== PDF RECEIPT ===");
        var pdfBuilder = new PdfReceiptBuilder();
        director.ConstructStandardReceipt(pdfBuilder, order);
        Console.WriteLine($"PDF saved at: {pdfBuilder.GetResult()}");
    }
}

Sample Output

=== HTML RECEIPT ===
<!DOCTYPE html><html><body>
<h1>GlobalMart</h1>
<p>Date: 2025-04-05</p>
<p>Customer: Alice Johnson (alice@example.com)</p>
<table border='1'><tr><th>Item</th><th>Qty</th><th>Price</th><th>Total</th></tr>
<tr><td>Laptop</td><td>1</td><td>$999.00</td><td>$999.00</td></tr>
...

=== TEXT RECEIPT ===
GlobalMart
Date: 2025-04-05
Customer: Alice Johnson <alice@example.com>
--------------------------------------------------
Laptop                       1 x  $999.00 =    $999.00
Mouse                        2 x   $25.00 =     $50.00
Keyboard                     1 x   $75.00 =     $75.00
--------------------------------------------------
Tax:                                   $12.50
Shipping:                               $5.99
TOTAL:                               $1,142.49
Thank you for shopping with us!

=== PDF RECEIPT ===
[PDF] Generated: receipt_a1b2c3d4e5.pdf (simulated)
PDF saved at: receipt_a1b2c3d4e5.pdf

Participants in the Builder Pattern

The classes and objects participating in this pattern include:

  • Product (OrderReceipt)
    • The complex object being being constructed.
    • Usually immutable.
  • Builder (IReceiptBuilder)
    • Declares a common construction interface for all steps.
  • ConcreteBuilder (HtmlReceiptBuilder, TextReceiptBuilder, PdfReceiptBuilder)
    • Implements the builder interface.
    • Provides specific implementation for each step.
    • Maintains the intermediate product.
    • Returns the final product.
  • Director (ReceiptDirector)
    • Orchestrates the construction steps using a Builder instance.
    • Optional — client can call builder directly.
  • Client
    • Configures the Director or calls Builder steps directly.
    • Chooses which ConcreteBuilder to use.

UML Class Diagram

Summary Table

ParticipantRole in E-Commerce Receipt
ProductOrderReceipt – final immutable receipt
BuilderIReceiptBuilder – common steps
ConcreteBuilderHtmlReceiptBuilder, TextReceiptBuilder, PdfReceiptBuilder
DirectorReceiptDirector – reusable construction logic
ClientProgram – selects format and triggers build

Advantages in E-Commerce

BenefitImpact
Fluent APIbuilder.AddItem(…).SetTax(…)
Reusable LogicDirector handles standard flow
ExtensibleAdd JsonReceiptBuilder easily
TestableMock IReceiptBuilder
Clean CodeNo 10-parameter constructors

Conclusion

The Builder pattern is perfect for generating multi-format documents like receipts, invoices, or reports in e-commerce. It provides:

  • Clarity in construction
  • Flexibility in output
  • Maintainability across formats

Used by frameworks like iText (PDF), Razor Templates (HTML), and StringBuilder patterns in .NET.

Uma Mahesh
Uma Mahesh

Author is working as an Architect in a reputed software company. He is having nearly 21+ Years of experience in web development using Microsoft Technologies.

Articles: 288