Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Builder Pattern in C#

0.00/5 (No votes)
1 Sep 2017 1  
Initialize a new type using Builder pattern in C#. Continue reading...

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 like:

[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 initialised, 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 is that the developer using the class has IntelliSense to guide them during coding:

Builder Pattern - Usage

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 setup 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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here