Continuing from our Design Patterns series, we will talk about the Builder Pattern – another creational pattern in this blog.

The Builder Pattern

Like other patterns mentioned in my previous blogs, the builder pattern is also used to create objects. The builder pattern helps build a complex object step by step without breaking the actual object. This helps in building different objects with varying properties with the same construction code.

Use Case

The main use of the builder pattern is that it reduces the need for a very big constructor with large numbers of parameters.

Let's look at an example. Here, we have a Person class that accepts constructor parameter as name, education, age, and height.

When we want to construct a person object without education, we have to pass null in place of education.  A simple demo is shown below:

using System;

namespace Patterns.Builder
{
    public class Person
    {
        private string _name;
        private int _age;
        private int _height;
        private EmploymentHistory _employmentHistory;

        public Person(string name, EmploymentHistory employmentHistory, int age, int height)
        {
            _name = name;
            _age = age;
            _height = height;
            _employmentHistory = employmentHistory;
        }

        public void ShowInfo()
        {
            Console.WriteLine($"Name : {_name}");
            Console.WriteLine($"Age : {_age}");
            Console.WriteLine($"Height : {_height}");
            _employmentHistory?.ShowInfo();
        }
    }

    public class EmploymentHistory
    {
        private int _totalJobsChanged;
        private string _currentJob;

        public EmploymentHistory(string currentJob , int totalJobsChanged)
        {
            this._totalJobsChanged= totalJobsChanged;
            this._currentJob = currentJob;
        }

        public EmploymentHistory()
        {
            _totalJobsChanged = 0;
            _currentJob = "None";
        }

        public void ShowInfo()
        {
            Console.WriteLine($"Current Job : {_currentJob} , total job changed : {_totalJobsChanged}");
        }
    }
}

using Patterns.Builder;

namespace Patterns
{
    internal static class Program
    {
        public static void Main(string[] args)
        {
    
            Person person = new Person("Apple", null, 0, 5);
            
            person.ShowInfo();
            
        }
        
    }
}

Above program outputs :
 ------ Person 1 -------
Name : Apple
Age : 0
Height : 5
 ------ Person 2 -------
Name : Ball
Age : 12
Height : 6
Current Job : Student , total job changed : 0
Builder Pattern Demo

The example shown above might look simple enough, but having many parameters in the constructor will make the code look hard to read.

Design Diagram

Builder Pattern

What if there were 10 , 15 or even 20+ parameters while constructing an object? You would get confused really fast about what parameter you are passing. So to solve this issue, we use a builder pattern.

Implementation

Let's go through implementing such a design pattern with an example.

First, we need to determine all the construction steps required for building an object. A simple interface for the builder will have all this steps defined. In my example, I have created a ICharacterBuilder class that has all steps for generating a character. Then,CharacterBuilder will implement from the ICharacterBuilder.

All methods used as steps to build a final product will return a builder reference, which is shown below by returning this in all methods. We need to add one method that will create a product based on steps taken and return a final product. I am using the Build method to create a Character object and return it.

We can also add director if we want, which will encapsulate creation of final product using builder object. The client will create both a builder and a director object and pass the builder to the director. The director will then take the required steps and produce a final product. In my implementation, I have not used a director. I am using the builder to directly create an object.

Code and Execution

using System;
using Patterns.Factory_Pattern.Factory_Method;

namespace Patterns.Builder
{
  	public interface ICharacterBuilder
    {
        CharacterBuilder SetName(string name);
        CharacterBuilder SetHealth(float health);
        CharacterBuilder SetLevel(float level);
        CharacterBuilder SetAbility(AbilityType abilityType);
    }
    
    public class CharacterBuilder
    {
        public string _name { get; private set; }
        public float _health { get; private set; }
        public float _level{ get; private set; }
        public AbilityType _abilityType { get; private set; }
        
        
        public CharacterBuilder SetName(string name)
        {
            _name = name;
            return this;
        }

        public CharacterBuilder SetHealth(float health)
        {
            _health = health;
            return this;
        }

        public CharacterBuilder SetLevel(float level)
        {
            _level = level;
            return this;
        }

        public CharacterBuilder SetAbility(AbilityType abilityType)
        {
            _abilityType = abilityType;
            return this;
        }

        public Character Build()
        {
            return new Character(this);
        }

    }

    public class Character
    {
        private string _name;
        private float _health;
        private float _level;
        private AbilityType _abilityType;


        public Character(string name = "", float health = 0, float level = 0,
            AbilityType abilityType = AbilityType.fire)
        {
            
        }
        
        public Character(CharacterBuilder characterBuilder)
        {
            _name = characterBuilder._name;
            _health = characterBuilder._health;
            _level = characterBuilder._level;
            _abilityType = characterBuilder._abilityType;
        }
        
        public void ShowStats()
        {
            Character c = new Character("", 0f, 0f, AbilityType.ice);
            Console.WriteLine($"Name : {_name}");
            Console.WriteLine($"Health : {_health}");
            Console.WriteLine($"Level : {_level}");
            Console.WriteLine($"Ability : {_abilityType}");
        }
    }
}

using System;
using Patterns.Factory_Pattern.Factory_Method;

namespace Patterns.Builder
{
    public class BuilderDemo
    {
        public static void Run()
        {
            CharacterBuilder characterBuilder = new CharacterBuilder();
            Character character = characterBuilder.SetLevel(1)
                .SetName("Gigachad").Build();
            Console.WriteLine("\n----------------------    Stats for character 1 -----------------------");
            character.ShowStats();


            Character character2 = characterBuilder.SetName("Binaya").SetAbility(AbilityType.slash).SetHealth(100)
                .SetLevel(15).Build();
            Console.WriteLine("\n----------------------    Stats for character 2 -----------------------");
            character2.ShowStats();
        }
    }
}

using Patterns.Builder;

namespace Patterns
{
    internal static class Program
    {
        public static void Main(string[] args)
        {
            BuilderDemo.Run();
        }
        
    }
}

----------------------    Stats for character 1 -----------------------
Name : Gigachad
Health : 0
Level : 1
Ability : fire

----------------------    Stats for character 2 -----------------------
Name : Binaya
Health : 100
Level : 15
Ability : slash
Builder Pattern Implementation
That's all for today, hope you learned something new from this. I will be writing about more patterns soon.