6 min. read

Highlight - The Builder Pattern

A creational design pattern created to decouple details into a friendly API for constructing an object.

Builder Design pattern

The builder design pattern focuses on specifying parts for the object and finally constrcuting it.
This allows a “Full” construction or Partial construction.

Diagram and Usage

IBuilder constructs abstractions based on parts added by construction methods - AddPartA() and AddPartB().

Finally Construct returns a concrete object of IObject.

A good addition to the pattern is returning the same object on construction so the API can be chained.
Example:
Builder.AddPart("one").AddPart("two").Build();

C# Example of the diagram

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public interface IObject { }

public interface IBuilder
{
IBuilder AddPartOne(string part);
IBuilder AddPartTwo(string part);
IObject Construct();
}

public class A : IObject
{
public string Content { get; set; }
}

public class ABuilder : IBuilder
{
private StringBuilder mBuilder = new StringBuilder();
private StringBuilder mBuilderTwo = new StringBuilder();

public IBuilder AddPartOne(string part)
{
mBuilder.Append(part);
return this;
}

public IBuilder AddPartTwo(string part)
{
mBuilderTwo.Append(part);
return this;
}

public IObject Construct()
{
return new A()
{
Content = $"{mBuilder.ToString()}\n{mBuilderTwo.ToString()}";
};
}
}

public class Program
{
public static void Main()
{
var builder = new ABuilder();
var a = builder.AddPartOne("A").AddPartTwo("B").Construct();
}
}

Example 1: Alchemy crafting

I like to use fantasy like examples for systems because it shows how creative you can get with these patterns.

Recipe Result
Leaves + Claws Swiftness
Fire + Bricks Fire bricks
Fire + Metal Hard Metal

We need an ingredient:

1
2
3
4
5
public class Ingredient
{
public string Name { get; set; }
public Guid ID { get; set; }
}

Now we need a recipe:

1
2
3
4
public class Recipe
{
public IReadonlyCollection<string> Ingredients {get; init;}
}

Let’s assume that our game engine contains an Item which is also an Ingredient:

1
2
3
4
public class Item : Ingredient
{

}

Now we need an alchemy engine:

1
2
3
4
5
public interface IAlchemyCraft
{
IAlchemyCraft Add(Ingredient ing);
Item Combine();
}

Now we can craft the alchemy crafting system based on recipes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class AlchemyRecipeCrafts : IAlchemyCraft
{
private IDictionary<Recipe, Func<Item>> mRecipes;
private IList<Ingredient> mIngredients;

public IAlchemyCraft Add(Ingredient ing)
{
mIngredients.Add(ing);
return this;
}

public Item Combine()
{
if(!HasRecipe())
{
return null;
}

Recipe recipe = new Recipe
{
Ingredients = mIngredients
};
var item = mRecipes[recipe]();
mIngredients.Clear();
return item;
}

public void AddRecipe(Recipe p, ItemFactory factory)
{
mRecipes.Add(p, factory);
}

private bool HasRecipe() { /* ... */}
}

AlchemyRecipeCrafts contains a list of current ingredients which are cleared when we combine them.
We need to create the recipes for the defined items.
Swiftness, Fire Brick and Hard Metal.

Usage:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
AlchemyRecipeCrafts crafting = new AlchemyRecipeCrafts();

Recipe swiftnessRecipe = new Recipe
{
Ingredients = new List<Item>() { new Leaves(), new Claws()}
};
crafting.AddRecipe(swiftnessRecipe, () => new Swiftness());

Recipe fireBricks = new Recipe
{
Ingredients = new List<Item>() { new Fire(), new Brick()}
};
crafting.AddRecipe(fireBricks, () => new FireBricks());

Recipe hardMetal = new Recipe
{
Ingredients = new List<Item>() { new Fire(), new Metal()}
};
crafting.AddRecipe(hardMetal, () => new HardMetal());

var swiftness = crafting.Add(new Leaves()).Add(new Claws()).Combine();

In this manner we can construct items dynamically.

This specific system lacks few things:

  1. Amount of items are not specified in Recipes.
  2. Item is a static class - to make it more generic we should construct item classes not by specific classes but by the generic class.
    For example: new Item() { Name="Claws", Type = ItemType.Ingredient };
    Of course it depends on the game engine.
  3. Configuring the recipes (For example with json) to create items.

Example 2: Abstracting SQL as a builder:

One of my favorite examples for this pattern is constructing SQL statements.

The system contains a table of people, this is the class:

1
public readonly record struct Person(string Name, int Age);

The statement builder:

1
2
3
4
5
6
7
8
9
10
11
public interface IStatementBuilder<T>
{
IEnumerable<T> Fetch();
}

public interface IPersonFetcher : IStatementBuilder<Person>
{
IStatementBuilder WithName(string name);
IStatementBuilder WithNames(IEnumerable<string> name);
IStatementBuilder BetweenAge(int min, int max);
}

Usage:

1
2
3
IPersonFetcher fetcher = new PersonFetcher();

var people = fetcher.WithNames(new string[] {"Bob","Alice"}).BetweenAge(20,60).Fetch();

The SQL built may look like so:

1
2
3
SELECT * FROM People
WHERE `name` like '%Bob%' OR `name` like '%Alice%'
AND Age > 20 AND Age < 60;

Alternatives

The builder design pattern allows to construct an item from many ingredients.
If we don’t need that kind of behavior we may use the alternatives:

  • Factory - Abstract away the creation process.
  • Prototype - Clones an object.
  • Singelton - Creates a single instance.

In the Alchemy example we actually used a factory lambda.
The Func<Item> is a factory for items using a lambda.

The Factory pattern is more common than the Builder pattern,
Yet I prefer the Builder for constructing more complex objects.

The SQL example is very pracical and as you can see it simplifies the usage of statement construction.


Thanks for reading!


Is this helpful? You can show your appreciation here: Buy me a coffee