Builder Pattern

The Builder pattern was created to solve some issues that arise with the Factory and Abstract Factory patterns, especially when an object has many attributes. Instead of having a complicated constructor with too many parameters, the Builder pattern allows you to build the object step by step, making the process cleaner and more manageable.

The Builder Pattern, as defined in the Gang of Four (GoF) book Design Patterns: Elements of Reusable Object-Oriented Software

"The Builder pattern is a design pattern focused on object creation, specifically helps in building complex objects step by step. Its main idea is to separate how an object is created from how it looks, so the same process can be used to create different versions of the object. "

Example:

Let's say we have a User class with properties such as UserName, UserEmail, UserPassword, and UserAge. We'll demonstrate how to use the builder pattern to create instances of the User class.

Step 1: Define the User Class.

This is a simple class with properties that we want to build using the Builder pattern.

public class User
{
    public string UserName { get; set; }
    public string UserEmail { get; set; }
    public string UserPassword { get; set; }
    public int UserAge { get; set; }

    // Here Constructor is private to prevent the  direct instantiation
    // Means : Private constructor to ensure that objects are created via the builder 
    private User() { }

    // Nested Builder class here is to help with the construction
    public class Builder
    {
       // Fields to store the values before constructing the User object
        private string userName; 
        private string userEmail;
        private string userPassword;
        private int userAge;

        public Builder SetUsername(string username)
        {
            this.userName = username;
            return this;
        }

        public Builder SetEmail(string email)
        {
            this.userEmail = email;
            return this;
        }

        public Builder SetPassword(string password)
        {
            this.userPassword = password;
            return this;
        }

        public Builder SetAge(string age)
        {
            this.userAge = age;
            return this;
        }

        public User Build()
        {
            return new User
            {
                UserName = this.userName,
                UserEmail =  this.userEmail,
                UserPassword =  this.userPassword,
                UserAge = this.userAge
            };
        }
    }
}

Step 2: Usage of the Builder Pattern.

Now, let’s use the Builder, and create a User object with different properties and display them.


class Program
{
    static void Main(string[] args)
    {
        // Using the Builder Pattern to create a User object
        // Create User 1 using the builder
        User user1 = new User.Builder()
                            .SetUsername("johnny_doje")
                            .SetEmail("johnny_doje@example.com")
                            .SetPassword("SecurePassword123")
                            .SetAge(30)
                            .Build();

        // Create User 2 using the builder
        User user2 = new User.Builder()
                            .SetUsername("alyca_smith")
                            .SetEmail("alyca.smith@example.com")
                            .SetPassword("AnotherPassword456")
                            .SetAge(25)
                            .Build(); 

        // Displaying the user details
        Console.WriteLine($"User 1: UserName = {user1.UserName}, UserEmail = {user1.UserEmail}, UserAge = {user1.UserAge}"); 
        Console.WriteLine($"User 2: UserName = {user2.UserName}, UserEmail = {user2.UserEmail}, UserAge = {user2.UserAge}"); 
    }
}

Output:


User 1: UserName = johnny_doje, UserEmail = johnny_doje@example.com, UserAge = 30 
User 2: UserName = alyca_smith, UserEmail = alyca_smith@example.com, UserAge = 25


Explanation of the Code:

  • User Class: The class contains private setters to ensure that the properties cannot be set directly from outside the class. The object is only created using the builder.
  • Builder Class: The Builder class helps in setting the properties for the User object. Each setter method returns the user instance, allowing for method chaining. The Build() method creates a new User object using the properties set in the builder.

Here are some real-world project scenarios where the Builder Pattern can be used in .NET:

  1. EmailBuilder
    • Used to construct an email with different components like subject, body, recipient, attachments, etc.
  2. ReportBuilder
    • Used to build complex reports (e.g., PDF, Word, Excel) with different sections, tables, and formats.
  3. DatabaseQueryBuilder
    • Used to dynamically create complex SQL queries based on its input parameters, filtering, and ordering options.
  4. DocumentBuilder
    • Used to create structured documents (e.g., HTML, XML, or JSON) from various components like headers, body etc.

Conclusion:

The Builder Pattern is a powerful tool which helps in managing the construction of complex objects. It basically helps in creating flexible, readable, and maintainable code, particularly when an object requires multiple/different parameters. The example with the User class demonstrates how to use the builder to create different user instances in a structured and cleaner way.