Builder
Intent
Separate the construction of a complex object from its representation so that the same construction process can create different representations.
Problem
You need to create complex objects step by step. The construction process must allow different representations of the object being constructed, without forcing the client to know the details of each step.
Real-World Analogy
Think of ordering a custom burger at a restaurant. You don’t get the whole burger at once — you choose the bun, then the patty, then toppings, then sauce, step by step. The cook (director) knows the sequence, and you can end up with completely different burgers using the same process. A vegetarian builder and a meat-lover builder follow the same steps but produce different results.
When You Need It
- You’re constructing complex configuration objects with many optional fields — like an HTTP request with headers, body, query params, and timeout that shouldn’t all be crammed into one constructor
- You’re generating reports or documents step-by-step (header, body, charts, footer) and want to reuse the same assembly process for PDF, HTML, and plain text output
- You’re building test data with many variations — “create a user with admin role, verified email, and two orders” vs. “create a basic guest user”
UML Class Diagram
classDiagram
class Director {
-builder: Builder
+construct()
}
class Builder {
<<interface>>
+buildPartA()
+buildPartB()
+getResult() Product
}
class ConcreteBuilder {
-product: Product
+buildPartA()
+buildPartB()
+getResult() Product
}
class Product {
+parts: List
}
Director o-- Builder
Builder <|.. ConcreteBuilder
ConcreteBuilder ..> Product : creates
Sequence Diagram
sequenceDiagram
participant Client
participant Director
participant Builder
Client->>Director: construct(builder)
Director->>Builder: buildPartA()
Director->>Builder: buildPartB()
Client->>Builder: getResult()
Builder-->>Client: product
Participants
| Participant | Role |
|---|---|
| Builder | Specifies an abstract interface for creating parts of a Product |
| ConcreteBuilder | Constructs and assembles parts of the product by implementing the Builder interface |
| Director | Constructs an object using the Builder interface |
| Product | Represents the complex object under construction |
How It Works
- The Director knows the construction steps and their order.
- The Builder interface defines all possible construction steps.
- ConcreteBuilders implement those steps for a specific product variant.
- The client creates a builder, passes it to the director, and retrieves the result from the builder.
Applicability
Use when:
- The algorithm for creating a complex object should be independent of the parts and how they’re assembled
- The construction process must allow different representations of the constructed object
- You want to construct objects step-by-step, potentially deferring or skipping some steps
Don’t use when:
- The product is simple with few parts
- Only one representation is needed
Trade-offs
Pros:
- Constructs complex objects step-by-step, allowing fine control over the process
- Same construction process can produce different representations
- Isolates complex construction code from business logic
Cons:
- Requires creating a separate builder class for each product type
- Can be verbose — especially for simple objects with few fields
- Director class may become unnecessary if clients drive the construction themselves
Example Code
C#
public class House
{
public int Walls { get; set; }
public bool HasRoof { get; set; }
public bool HasGarage { get; set; }
public override string ToString() =>
$"House: {Walls} walls, roof={HasRoof}, garage={HasGarage}";
}
public interface IHouseBuilder
{
void BuildWalls(int count);
void BuildRoof();
void BuildGarage();
House GetResult();
}
public class ConcreteHouseBuilder : IHouseBuilder
{
private House _house = new();
public void BuildWalls(int count) => _house.Walls = count;
public void BuildRoof() => _house.HasRoof = true;
public void BuildGarage() => _house.HasGarage = true;
public House GetResult() => _house;
}
public class Director
{
public void ConstructSimpleHouse(IHouseBuilder builder)
{
builder.BuildWalls(4);
builder.BuildRoof();
}
public void ConstructFancyHouse(IHouseBuilder builder)
{
builder.BuildWalls(6);
builder.BuildRoof();
builder.BuildGarage();
}
}
Delphi
type
THouse = class
Walls: Integer;
HasRoof: Boolean;
HasGarage: Boolean;
function ToString: string; override;
end;
IHouseBuilder = interface
procedure BuildWalls(Count: Integer);
procedure BuildRoof;
procedure BuildGarage;
function GetResult: THouse;
end;
TConcreteHouseBuilder = class(TInterfacedObject, IHouseBuilder)
private
FHouse: THouse;
public
constructor Create;
procedure BuildWalls(Count: Integer);
procedure BuildRoof;
procedure BuildGarage;
function GetResult: THouse;
end;
TDirector = class
procedure ConstructSimpleHouse(Builder: IHouseBuilder);
procedure ConstructFancyHouse(Builder: IHouseBuilder);
end;
function THouse.ToString: string;
begin
Result := Format('House: %d walls, roof=%s, garage=%s',
[Walls, BoolToStr(HasRoof, True), BoolToStr(HasGarage, True)]);
end;
constructor TConcreteHouseBuilder.Create;
begin
FHouse := THouse.Create;
end;
procedure TConcreteHouseBuilder.BuildWalls(Count: Integer);
begin FHouse.Walls := Count; end;
procedure TConcreteHouseBuilder.BuildRoof;
begin FHouse.HasRoof := True; end;
procedure TConcreteHouseBuilder.BuildGarage;
begin FHouse.HasGarage := True; end;
function TConcreteHouseBuilder.GetResult: THouse;
begin Result := FHouse; end;
procedure TDirector.ConstructSimpleHouse(Builder: IHouseBuilder);
begin
Builder.BuildWalls(4);
Builder.BuildRoof;
end;
procedure TDirector.ConstructFancyHouse(Builder: IHouseBuilder);
begin
Builder.BuildWalls(6);
Builder.BuildRoof;
Builder.BuildGarage;
end;
C++
#include <iostream>
#include <string>
#include <memory>
struct House {
int walls = 0;
bool hasRoof = false;
bool hasGarage = false;
void show() const {
std::cout << "House: " << walls << " walls, roof="
<< hasRoof << ", garage=" << hasGarage << "\n";
}
};
class IHouseBuilder {
public:
virtual ~IHouseBuilder() = default;
virtual void buildWalls(int count) = 0;
virtual void buildRoof() = 0;
virtual void buildGarage() = 0;
virtual std::unique_ptr<House> getResult() = 0;
};
class ConcreteHouseBuilder : public IHouseBuilder {
std::unique_ptr<House> house_ = std::make_unique<House>();
public:
void buildWalls(int count) override { house_->walls = count; }
void buildRoof() override { house_->hasRoof = true; }
void buildGarage() override { house_->hasGarage = true; }
std::unique_ptr<House> getResult() override { return std::move(house_); }
};
class Director {
public:
void constructSimpleHouse(IHouseBuilder& builder) {
builder.buildWalls(4);
builder.buildRoof();
}
void constructFancyHouse(IHouseBuilder& builder) {
builder.buildWalls(6);
builder.buildRoof();
builder.buildGarage();
}
};
Runnable Examples
| Language | Source |
|---|---|
| C# | Builder.cs |
| C++ | builder.cpp |
| Delphi | builder.pas |
Related Patterns
- Abstract Factory — Builder focuses on step-by-step construction; Abstract Factory emphasizes families of products
- Composite — Builders often build Composites