Design Patterns - Builder

25 maart 2022 om 10:00 by ParTech Media - Post a comment

In our previous blog of the design pattern series, we discussed the Singleton design pattern, which falls under the category of creational design patterns. Today, we will explore another design pattern from the same category called Builder Design Pattern.

Table of contents

  1. Builder Design Pattern
  2. Implementation of Builder Design Pattern
  3. Final Thoughts

Builder Design Pattern

The builder design pattern is one of the creational design patterns which is used to handle the complex setup involved in creating an object. As the name suggests, builder patterns follow a step-by-step approach to create an object. It is independent of object creation.

There might be cases where more than one constructor parameter is required to create an object. In such cases, the complexity revolves around how many constructor parameters can be sent so that it would be more readable.

Imagine sending 10+ parameters to a constructor for creating the object. In such cases, code smell tools throw them as issues as it is an indication that the class depends on a lot of external factors to complete its job. This is against the single responsibility principle. This is where Builder Design Pattern helps. It is ideal for scenarios where up to 7 parameters need to be handled when creating an object.

Implementation of Builder Design Pattern

Builder design pattern mainly consists of four parts - Builder, ConcreteBuilder, Product and Director.

namespace PARTECH_BuilderPattern
{
    internal class Program
    {
        static void Main(string[] args)
        {
            var director = new Director(new ConcreteBuilder());
            director.Construct();
        }
    }

    public interface IBuilder
    {
        void Step1();
        void Step2();
        void Step3();
        Product GetProduct();
    }

    public class Product
    {
        public string Data1 { get; set; }
        public string Data2 { get; set; }
        public string Data3 { get; set; }
    }

    public class ConcreteBuilder : IBuilder
    {
        private Product product = new Product();
        public void Step1()
        {
            product.Data1 = "1";
        }

        public void Step2()
        {
            product.Data1 = "2";
        }

        public void Step3()
        {
            product.Data1 = "3";
        }

        public Product GetProduct()
        {
            return product;
        }
    }

    public class Director
    {
        private IBuilder builder;

        public Director(IBuilder builder)
        {
            this.builder = builder;
        }
        public void Construct ()
        {
            builder.Step1();
            builder.Step2();
            builder.Step3();
            var product = builder.GetProduct();
        }
    }
}

In the above code, the builder, the base of the code, is an interface where the steps or procedures involved in creating an object are provided.

A Product class is created, inside which the properties of the product class are provided. This is mainly for the object creation that is going to be done.

A concrete builder class is created where it inherits the Builder interface and implements the methods that are provided in the builder. It initializes the Product class inside it and provides the required values in the methods that are implemented.

The director-class contains a constructor which takes up the builder interface object as input. And it also has a construction method where the methods required to create an object are called one by one. And in the last steps, the Product is obtained.

Now, let's now see how the director-class is instantiated.

In the main method, a new director of the concrete builder is provided, and the Construct method defined under is called.

And that’s the template of the Builder design pattern. Let's now understand the same with the help of a practical scenario.

Consider a website that shows data for different kinds of mobiles available in the market. And they do random customization for each model. Now they need to create objects for different mobiles. Let us see how to implement the builder pattern for this scenario.

namespace PARTECH_BuilderPattern
{
    internal class Program
    {
        static void Main(string[] args)
        {
            var mobile = new MobileCreator(new Blue());
            mobile.GetMobile();
        }
    }

    public interface IMobile
    {
        void SetModel();
        void SetProcessor();
        void SetPorts();
        void SetManufactureYear();
        Mobile GetMobile();

    }

    public class Mobile
    {
        public string Model { get; set; }
        public string Processor { get; set; }
        public int Ports { get; set; }

        public int Year { get; set; }
    }

    public class Blue : IMobile
    {
        private Mobile mobile = new Mobile();
        public void SetModel()
        {
            mobile.Model = "X";
        }
        public void SetProcessor()
        {
            mobile.Processor = "ABC";
        }
        public void SetPorts()
        {
            mobile.Ports = 1;
        }
        public void SetManufactureYear()
        {
            mobile.Year = 2022;
        }
        public Mobile GetMobile()
        {
            return mobile;
        }
    }

    public class Orange : IMobile
    {
        private Mobile mobile = new Mobile();
        public void SetModel()
        {
            mobile.Model = "AB";
        }
        public void SetProcessor()
        {
            mobile.Processor = "XYZ";
        }
        public void SetPorts()
        {
            mobile.Ports = 2;
        }
        public void SetManufactureYear()
        {
            mobile.Year = 2021;
        }
        public Mobile GetMobile()
        {
            return mobile;
        }
    }

    public class MobileCreator
    {
        private IMobile mobile;

        public MobileCreator(IMobile mobile)
        {
            this.mobile = mobile;
        }

        public Mobile GetMobile()
        {
            mobile.SetModel();
            mobile.SetProcessor();
            mobile.SetPorts();
            mobile.SetManufactureYear();
            return mobile.GetMobile();
        }

    }

}

In the above code, IMobile is the IBuilder interface. Mobile is the Product class that stores the properties of the mobile. Blue and Orange are the mobile brands that are the concrete builders here, while MobileCreator is the Director.

IMobile holds methods for setting model name, processor, ports, year, and get the object of the mobile. The interface IMobile is consumed by two classes viz. Blue and Orange. And inside each of them, respective values are hardcoded, and their object is returned. Mobile is a model that helps in returning the model from the GetMobile method. MobileCreator has a constructor which takes up the IMobile (IBuilder) as input. It has a method where the steps for creating the mobile are called one after the other. In the end, the product is obtained.

To consume the created object from the main method, the director (MobileCreator) is called by passing the type of ConcreteBuilder object. This is required for the code processing (in our case, Blue and Orange), which returns the object.

With this setup, it is possible to omit certain steps in object creation. This is done by having conditions in the Director class, making it independent of the object creation and the values that are assigned to it.

Final Thoughts

And that’s what a Builder creational design pattern is all about. We have also seen how we can implement it in a real-world scenario. Stay tuned for the next blog on Design Patterns series.

Nieuwste