Dot Net For All

Builder Pattern – The Different versions

Hello, In this article I will discuss about one of the simplest considered pattern after singleton pattern. This is the builder pattern. I will discuss here two variations or versions of the builder pattern with C# examples.

What is Builder Pattern

As per Gang of four definition “Separate the construction of a complex object from its representation so that the same construction process can create different representations.”

Why Should you use Builder Pattern

As read in the definition, the builder and director will take care of the building of the instance of the class(Product). There can be various representations of the product which will be decided by the builder and no one else.

In this section I will use the director which in turn will use the builder to create the product.

The builder and director helps to get the final product. There can be various builders and directors working together to get the final product as result.

I want to design a system which has a single product named Car. But the car have different variants i.e. Economy, Medium and Luxury. These variants will vary as per the features like Wheels, Upholstery, Color and other properties.

There are some defined steps which we need to perform before getting the end result like first add the wheel, then upholstery, followed by color and so on.

Builder Pattern

 

As seen in the above figure the Client only knows about the director and builder class.

The builder interface will have different version which will adhere to the creation process.

 public enum CarModel
    {
        Economy,
        Medium,
        Luxury
    }

    public enum Wheels
    {
        Alloy,
        Metal,       
        Polished
    }

    public enum Upholstery
    {
        Normal,
        RayonFabric,
        Leather    
    }

    public enum Color
    {
        Normal,
        Metallic,
        Glossy
    }

    public enum AirConditioning
    {
        Manual,
        Automatic,
        ClimateControl
    }

    public class Director
    {
        public void Construct(ICarBuilder car)
        {
            car.CarType();
            car.ProvideACType();
            car.ProvideColorType();
            car.ProvideUpholsteryType();
            car.ProvideWheelType();
        }
    }

    public class Car
    {
        public CarModel Model { get; set; }
        public Wheels WheelType { get; set; }
        public Upholstery Upholstery { get; set; }
        public Color Color { get; set; }
        public AirConditioning AirConditioning { get; set; }

        public void Display()
        {
            Console.WriteLine(string.Format("Model: {0}", Model));
            Console.WriteLine(string.Format("WheelType: {0}", WheelType));
            Console.WriteLine(string.Format("Upholstery: {0}", Upholstery));
            Console.WriteLine(string.Format("Color: {0}", Color));
            Console.WriteLine(string.Format("AirConditioning: {0}", AirConditioning));
        }
    }

    public interface ICarBuilder
    {
        void CarType();
        void ProvideWheelType();
        void ProvideUpholsteryType();
        void ProvideColorType();
        void ProvideACType();

        Car GetCar();
    }

    public class NormalCarBuilder : ICarBuilder
    {
        private Car myCar = new Car();

        public void CarType()
        {
            myCar.Model = CarModel.Economy;
        }

        public Car GetCar()
        {
            return myCar;
        }

        public void ProvideACType()
        {
            myCar.AirConditioning = AirConditioning.Manual;
        }

        public void ProvideColorType()
        {
            myCar.Color = Color.Normal;
        }

        public void ProvideUpholsteryType()
        {
            myCar.Upholstery = Upholstery.Normal;
        }

        public void ProvideWheelType()
        {
            myCar.WheelType = Wheels.Metal;
        }
    }

    public class MediumCar : ICarBuilder
    {
        private Car myCar = new Car();

        public void CarType()
        {
            myCar.Model = CarModel.Medium;
        }

        public Car GetCar()
        {
            return myCar;
        }

        public void ProvideACType()
        {
            myCar.AirConditioning = AirConditioning.Automatic;
        }

        public void ProvideColorType()
        {
            myCar.Color = Color.Metallic;
        }

        public void ProvideUpholsteryType()
        {
            myCar.Upholstery = Upholstery.Leather;
        }

        public void ProvideWheelType()
        {
            myCar.WheelType = Wheels.Alloy;
        }
    }

    public class LuxaryCar : ICarBuilder
    {
        private Car myCar = new Car();

        public void CarType()
        {
            myCar.Model = CarModel.Luxury;
        }

        public Car GetCar()
        {
            return myCar;
        }

        public void ProvideACType()
        {
            myCar.AirConditioning = AirConditioning.ClimateControl;
        }

        public void ProvideColorType()
        {
            myCar.Color = Color.Glossy;
        }

        public void ProvideUpholsteryType()
        {
            myCar.Upholstery = Upholstery.RayonFabric;
        }

        public void ProvideWheelType()
        {
            myCar.WheelType = Wheels.Polished;
        }
    }

As you can see in the above code, the builder has different implementations.

These are Economy, Medium, and Luxury. All the individual classes which implement the ICarBuilder and return the final product.

Now it depends on the client which variant of Car does he need.

        static void Main(string[] args)
        {
            ICarBuilder normalCarBuilder = new NormalCarBuilder();            

            Director director = new Director();
            director.Construct(normalCarBuilder);

            Car normalCar = normalCarBuilder.GetCar();
            normalCar.Display();

            Console.Read();
        }

In the above code the client needs only the Normal car. If you are the client and want some other variant of the Car, you can use any other variant.

Now suppose if the car company plans to come up with some fourth variant, you can guess how easy it is to extend the above design to get some new variant keeping the construction process same.

Or if we want to change the building process, in that case the change is also very simple.

We can use this version of builder pattern when

Building Class using Fluent Syntax and Omitting Multiple Constructor Arguments

There is one more utilization of builder pattern. In many cases we may encounter a class needing many constructor arguments and sometimes it becomes highly cumbersome to have too  many parameters for a c# class constructor.

It becomes highly confusing for the users of the class which has too many constructors. The user of the class does not know which constructor argument to pass and which one to skip to get the desired instance of the class.

And it can be highly error prone if we just interchange any of the argument while creating the instance.

As an example lets see the below class code.

    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string City { get; set; }
        public string Country { get; set; }
        public string Initials { get; set; }
        public string Occupation { get; set; }

        public Person(string firstName, string lastName, string city, string country, 
        string initials, string occupations)
        {
            this.FirstName = firstName;
            this.LastName = lastName;
            this.City = city;
            this.Country = country;
            this.Initials = initials;
            this.Occupation = occupations;
        }
    }
}

While creating an instance of the above class the client has to pass all the constructor arguments. What if by mistake the client interchanges the city and country. The whole functionality will be wrong.

What if a requirement comes that the Person should be initialized with firstName and LastName only. In that case you have to write one more constructor.

To overcome all these problems there is one more version of the builder pattern as shown in the code below.

We can call it as fluent syntax for class creation using builder. The main class has a private constructor so that the instance cannot be created outside of the class. The builder is the nested class which has individual methods for each constructor parameter of the previous example.

Please check the code below.

    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string City { get; set; }


        private Person()
        {
            Console.WriteLine("New Person Created");
        }

        public class PersonBuilder
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public string City { get; set; }
           

            public PersonBuilder AddFirstName(string firstName)
            {
                FirstName = firstName;
                return this;
            }

            public PersonBuilder AddLastName(string firstName)
            {
                LastName = firstName;
                return this;
            }

            public PersonBuilder AddCity(string city)
            {
                City = city;
                return this;
            }

            public Person GetPerson()
            {
                Person person = new Person();
                person.FirstName = this.FirstName;
                person.LastName = this.LastName;
                person.City = this.City;
                return person;
            }
        }
    }

Now at the client we can call the above code as shown below. You can see that the client is creating two instances of the Person object. One with City and other without city.

 static void Main(string[] args)
        {
            Person p1 = new Person.PersonBuilder().AddFirstName("Vikram").AddLastName("chaudhary").GetPerson();
            Person p2 = new Person.PersonBuilder().AddFirstName("Manish").AddLastName("Jain").AddCity("Delhi").GetPerson();
        }

Its an exercise to the reader to add more parameters to the Person class in the above code and test it.

As you can see the above code makes it very easy for the user of the class to create the instances of the class without getting confused. And he can explicitly provide the arguments which he wants.

But there is no director present in this version of the builder pattern.

Conclusion:

In this article I have shown you the two versions of the builder pattern in C#. It depends on the kind of scenario where you want to use the pattern. If you want to follow some steps to create an instance of the complex object you can go with the builder pattern with director. If not you can simply use the next version.

Top career enhancing courses you can't miss

My Learning Resource

Excel your system design interview