Builder Design Pattern

Builder is categorized as a Creational Design Pattern. Why is it so? Because it is used to create objects in some way. In what way exactly? This article intends to explain that. If something is unclear to you, please ask it in the comments. The main problem is that there are so many extremely and unnecessarily complex examples that this design pattern (as well as other ones) becomes one of the hard things. However, this should not be so. Builder is pretty easy when you understand what it does.

First of all, we have to know what is the difference between object and class. Object itself is an instance of the class, i.e. some operator (it is called “new” in most of the OOP languages) is able to use a definition of the class (variables, methods), and based on that to assign some space in memory where both data and both instructions are defined. If you want to know more, you should definitely look into Assembly and its’ principles. However, point of this article is to explain Builder.

So, you know pretty basically (understanding can be simple, but in this case it is even correct one) what an object is. So, what is a class? I have already explained that, either. So, to make things clear, I should just repeat the same thing. The class is some definition defining data, data types and methods that an instantiated object will be able to perform. In other words, you can treat a class from a mathematical point of view, i.e. you can treat the class as a set of objects that instantiate from the class defined.

Why do we need to know that? Because Builder itself is also a class, and we should talk about exact definition of the Builder itself. And here we come to even more abstract way of thinking – definition of the class itself. What is it? Well, it is simple. Definition of the class is an interface, and class is the instance of the interface. So, what is the interface for Builder? The most abstract interface for Builder can be written in the following way:

interface IBuilder<T> {
    T build();
}

And that is it. T is some type of the class we want our builder to be able to build.

But why do we need building at all? Can’t we just write everything like that? For instance, we have class called “Customer” with some fields, and we just define everything we need. In that same “Customer”, we define multiple methods setting values, and so on. Actually, we can. And I will show how you can do that. Below is the following code that does exactly that:

class Customer {
    public Field1 { get; set; }
    public Field2 { get; set; }
    public Field3 { get; set; }
    public Field4 { get; set; }
    public Field5 { get; set; }
    public Field6 { get; set; }
    public Field7 { get; set; }
    public Field8 { get; set; }
    public Field9 { get; set; }
    public Field10 { get; set; }
}

How the one would instantiate such a massive class? Well, like this:

var customer = new Customer();
customer.Field1 = "Some value 1";
customer.Field2 = "Some value 2";
customer.Field3 = "Some value 3";
customer.Field4 = "Some value 4";
customer.Field5 = "Some value 5";
customer.Field6 = "Some value 6";
customer.Field8 = "Some value 8";
customer.Field9 = "Some value 9";
customer.Field10 = SHA512(customer.Field8 + customer.Field9);

Oops, we forgot to set Field7. Also, we see that Field10 can be logically deduced from Field8 and Field9, so why are we setting it still here? And what if the algorithm to set the value will change? Well, we are going to rewrite this code all over again. And maybe we will end up in a situation where there is going to be 2-3 places of the same repeating logic. After that, we will invent some method that abstracts all of this logic, and this method basically will be some sort of Builder. So, in any case, even the worst developers that want their code to shine are going to invent Builder itself by themselves. And this is the reason why Builder is a pattern.

So, seeing this use of code we see that Builder is a good Design Pattern for the following purposes:

  1. It allows to abstract data items in the class from the value setting and manipulation methods.
  2. Since Builder is a class, we can use it as an interface somewhere else. Also, we can add multiple dependencies (such as the one as algorithm used in setting the Field10).
  3. Potential API client does not need to worry about internals of setting the class. Client might concentrate just on the data in the class itself and that’s it.
  4. We can guarantee that builder will return the finalized state of the class, i.e. the object that is really correct and integral.

However, there is one “drawback” in the Builder design pattern. By introducing a Builder, you also increase the number of lines of code. However, I would not call it as a drawback. When introducing any of the class, any line of code, you are boosting size of your code base. So, basically the whole programming can be treated as a one big “drawback” if you think that adding more lines to code is a “drawback” by itself.

Finally, how to implement the Builder design pattern? As we saw before, we have an interface for the abstract Builder that is able to build instances of class defined by type T. So, we implement this abstract interface for Customer class:

class CustomerBuilder {
    private var readonly _customer = new Customer();
 
    public Customer build() {
        // Here, we can ensure that _customer will be of finalized state. For instance, we can throw an exception marking that something is not set.
        return _customer;
    }
}

But wait, is that really it? Actually, no. No one is going to use abstract builder interface directly. Most of people are going to instantiate some concrete builder themselves through “new” operator or its’ abstraction called “IoC Containers”. And these concrete builders are going to have pretty solid methods. For instance, we can add the method that sets both Field8 and Field9:

class CustomerBuilder {
    private var readonly _customer = new Customer();
 
    public CustomerBuilder setFields(string field8, string field9) {
        customer.Field8 = field8;
        customer.Field9 = field9;
        customer.Field10 = SHA512(field8 + field9);
 
        return this;
    }
 
    public Customer build() {
        // Here, we can ensure that _customer will be of finalized state. For instance, we can throw an exception marking that something is not set.
        return _customer;
    }
}

There is one important thing. Actually, it is not really important for the Builder design pattern, but it is really comfortable to do so: When adding some setter method, return instance of the Builder itself, so we would be able to chain all of the Builder methods.

And that is it. Builder design pattern implemented. So, when to use it exactly? Basically, use it whenever you feel that you have pretty complex looking class (like 5-10 or more fields), and you might expect that setting values for the fields is going to be painful, and this setting logic can be reused. So, as a general rule, you can make your decision based on answers to these questions:

  1. Does the class have at least five fields? If so, you might want to use Builder.
  2. Can we set values for the class fields based on other field values? If the 1st is yes, and this is yes, then you should definitely use Builder Design Pattern.

Thanks for reading, and I hope that now you really know what is the Builder, how to use it, and when to use it.

Leave a Reply

Your email address will not be published. Required fields are marked *