Problem
Initialize a new type using Builder Pattern in C#.
Solution
Create a simple immutable type.
- public sealed class Greeting
- {
- private readonly string timeOfDay;
- private readonly string to;
-
- public Greeting(string timeOfDay, string to)
- {
- if (string.IsNullOrEmpty(timeOfDay))
- throw new ArgumentException("Time of Day must be set");
-
- if (string.IsNullOrEmpty(to))
- throw new ArgumentException("To must be set");
-
- this.timeOfDay = timeOfDay;
- this.to = to;
- }
-
- public string Message => $"Good {timeOfDay} {to}";
- }
Create a builder for this type.
- public interface IGreetingBuilderSetTimeOfDay
- {
- IGreetingBuilderSetTo GreetingTimeOfDay(string timeOfDay);
- }
-
- public interface IGreetingBuilderSetTo
- {
- IGreetingBuilder GreetingTo(string to);
- }
-
- public interface IGreetingBuilder
- {
- Greeting Build();
- }
-
- public sealed class GreetingBuilder :
- IGreetingBuilderSetTimeOfDay,
- IGreetingBuilderSetTo,
- IGreetingBuilder
- {
- private string timeOfDay = "";
- private string to = "";
-
- private GreetingBuilder() {}
-
- public static IGreetingBuilderSetTimeOfDay CreateNew()
- {
- return new GreetingBuilder();
- }
-
- public IGreetingBuilderSetTo GreetingTimeOfDay(string timeOfDay)
- {
- this.timeOfDay = timeOfDay;
- return this;
- }
-
- public IGreetingBuilder GreetingTo(string to)
- {
- this.to = to;
- return this;
- }
-
- public Greeting Build()
- {
- return new Greeting(timeOfDay, to);
- }
- }
The builder can be used in the following way.
- [Fact(DisplayName = "Building_a_greeting_setups_up_greeting_message")]
- public void Building_a_greeting_setups_up_greeting_message()
- {
- Greeting greeting = GreetingBuilder
- .CreateNew()
- .GreetingTimeOfDay("Morning")
- .GreetingTo("James Bond")
- .Build();
-
- Assert.Equal(
- expected: "Good Morning James Bond",
- actual: greeting.Message);
- }
Discussion
The idea behind an immutable type, as discussed in a previous post, is to create a class that once initialized, can’t be mutated (modified). For builder pattern to work, you don’t have to create an immutable type. However, I prefer to create value objects in the domain as immutable to simplify testing and avoid concurrency issues.
Method Chaining
The builder class has a method to set each ‘part’ of the type it will build. These methods return the builder itself in order to chain methods easily.
Notice that methods don’t return concrete builder; instead, they return an interface to indicate the ‘next’ step in the build process. The advantage of it is that developers using the class have IntelliSense to guide them during coding.
Usage
The example above is trivial and the whole idea of using builder pattern may seem contrived. However, I’ve found this pattern useful when developing libraries that other developers will use in your team. It ensures that your services are set up correctly.
More interesting examples are within ASP.NET Core framework (e.g. WebHostBuilder
) and a simple Azure library I wrote. The linked source code for this post also has an EmailBuilder, a bit more interesting example.
Source Code
GitHub