-->

16/10/2011

Builder Pattern

Developers have observed that, composing a system using inheritance makes it too rigid.
Creational Pattern is a way to break that tight coupling by abstract out the object creation.
The Creational patterns aim to separate a system from how its objects are created, composed, and represented. They increase the system's flexibility in terms of the what, who, how, and when of object creation. Creational patterns encapsulate the knowledge about which classes a system uses, but they hide the details of how the instances of these classes are created and put together.

Lets start this pattern with a hot snac. "Pizzaaaa . . . "
 Even though i am a non vegetarian, i love "Veggi Delight" than any other flavor. I know its very hard to discuss a design pattern keeping this picture in front of you. But lets stick to topic.
There are lot of variations in Pizza, different sizes, there are different types of bread doughs(base), different types of sauces , different types of toppings that can be used to prepare a pizza.
Now let's cook a pizza in c# mode.



Factory Pattern : This pattern suites best when you are dealing with a family of different yet related and dependent objects.
Eg: Car, Bus, AirCraft will come under Vehicles.
Builder Pattern : This pattern suites best when you are dealing with same object but having different flavors and characteristics.
Eg: Pizza having different flavors and toppings.

Objective: To create infrastructure to handle pizza having:
two falvors "Mexican" & "American".
two different types of crusts "Thin" & "Thick".
two different sizes "Small" & "Big"
three different types of Sauces "Sweet","Hot"&"Italian"
5 different types of toppings "Monies", "Pepperoni", "Cheese", "Bacon" &  "Chicken" based on type of  flavor user selected.

What will happen if i try to implement the same using Factory Pattern instead of Builder?
You can. By doing so, you are treating different flavors of pizza as different objects.
Whats the problem in that?
Yes. There is a problem. When you deal with different concrete factories, you will end up creating constructors for all different no. of parameters in each of your concrete factories.
The bigger problem will be where ever you instantiate a object for these concrete factories, the user have to replicate the same no. of parameters and order.

Just like below:
public class Pizza
    {
        public Pizza(int size) { ... }
        public Pizza(int size, bool cheese) { ... }
        public Pizza(int size, bool cheese, bool pepperoni) { ... }
        public Pizza(int size, bool cheese, bool pepperoni,bool pickle) { ... }
        . 
        .
        .    
    }
But the programing and usage will go out of control as the no of parameters increases.


Lets discuss about different classes and their responsibilities in builder pattern.
Builder
Abstract interface for creating objects (product).
Concrete Builder
Provides implementation for Builder. It is an object able to construct other objects. Constructs and assembles parts to build the objects.
Director
The Director class is responsible for managing the correct sequence of object creation. It receives a Concrete Builder as a parameter and executes the necessary operations on it.
Product
The final object that will be created by the Director using Builder.
What i will get if i use Builder Pattern?
Here we start our journey by accepting that we have to create one single product but with different flavors. So, we will create two different builder classes which in turn creates a single object of a class called Pizza.
Go through the code below and you will understand the difference.

Step 1: Create the Builder.
namespace NewBuilderPizzaSample
{
    public class Builder
    {

        private string _type;
        private string _size;
        private string _sauce;
        private bool _cheese = false;
        private bool _pepperoni = false;
        private bool _bacon = false;
        private bool _chicken = false;
        private bool _meonies = false;
        private bool _bulkcrust = false;

        internal string Type { get { return _type; } }
        internal string Size { get { return _size; } }
        internal string Sauce { get { return _sauce; } }
        internal bool Cheese { get { return _cheese; } }
        internal bool Pepperoni { get { return _pepperoni; } }
        internal bool Bacon { get { return _bacon; } }
        internal bool Chicken { get { return _chicken; } }
        internal bool Meoinies { get { return _meonies; } }
        internal bool BulkCrest { get { return _bulkcrust; } }

        public Builder SetType(string type)
        {
            _type = type;
            return this;
        }
        public Builder SetSize(string size)
        {
            _size = size;
            return this;
        }
        public Builder SetSauce(string sauce)
        {
            _sauce = sauce;
            return this;
        }
        public Builder IsCheeseRequired(bool value)
        {
            _cheese = value;
            return this;
        }

        public Builder IsPepperoniRequired(bool value)
        {
            _pepperoni = value;
            return this;
        }

        public Builder IsBaconRequired(bool value)
        {
            _bacon = value;
            return this;
        }

        public Builder IsMeoniesRequired(bool value)
        {
            _meonies = value;
            return this;
        }

        public Builder IsChickenRequired(bool value)
        {
            _chicken = value;
            return this;
        }

        public Builder IsBulkCrustRequired(bool value)
        {
            _bulkcrust = value;
            return this;
        }
    }
}
Step 2: Create a Product
namespace NewBuilderPizzaSample
{
    public class NewPizza
    {
        private string type;
        private string size;
        private string sauce;
        private bool cheese;
        private bool pepperoni;
        private bool bacon;
        private bool chicken;
        private bool meonies;
        private bool bulkcrust;

        public NewPizza(Builder builder)
        {
            type = builder.Type;
            size = builder.Size;
            sauce = builder.Sauce;
            cheese = builder.Cheese;
            pepperoni = builder.Pepperoni;
            bacon = builder.Bacon;
            chicken = builder.Chicken;
            meonies = builder.Meoinies;
            bulkcrust = builder.BulkCrest;
        }

        public void DisplayOrder()
        {
            Console.WriteLine(System.Environment.NewLine);
            Console.WriteLine("/********************************************/");
            Console.WriteLine("Pizza Type: " + type);
            Console.WriteLine("Pizza Size: " + size);
            Console.WriteLine("Sauce Type: " + sauce);
            string strItems = "Consists of :"
                 + (bulkcrust ? " Bulk Crust;" : " Thin Crust;")
                 + (chicken ? " Chicken;" : "")
                 + (cheese ? " Cheese;" : "")
                 + (pepperoni ? " Pepperoni;" : "")
                 + (bacon ? " Bacon;" : "")
                 + (meonies ? " Meonies;" : "");
            Console.WriteLine(strItems);
            Console.WriteLine("/********************************************/");
        }

    }
}
Step 4: Create Concrete builders. The actual logic lies here. Defining the parameters as per the flavor. Please not that in my example, i ensured that we can define any number on parameters in one go.
American : Small Size; Sweet Sauce; Bulk Crust; Chicken, Cheese & Meonies as toppings
namespace NewBuilderPizzaSample
{
    public class AmericanBuilder : Builder
    {
        public Builder _builder;
        public AmericanBuilder()
        {
            _builder = new Builder()
                .SetType("American")
                .SetSize(SizeType.Small)
                .SetSauce(SauceType.Sweet)
                .IsBulkCrustRequired(true)
                .IsCheeseRequired(true)
                .IsChickenRequired(true)
                .IsMeoniesRequired(true);
        }
    }
}
Mexican : Big Size; Hot Sauce; Thin Crust; Chicken, Pepperoni, Bacon & Meonies as toppings
namespace NewBuilderPizzaSample
{
    public class MexicanBuilder : Builder
    {
        public Builder _builder;
        public MexicanBuilder()
        {
            _builder = new Builder()
                .SetType("Mexican")
                .SetSize(SizeType.Big)
                .SetSauce(SauceType.Hot)
                .IsBaconRequired(true)
                .IsChickenRequired(true)
                .IsMeoniesRequired(true)
                .IsPepperoniRequired(true);
        }

    }
}
Step 5: Create some supporting classes for different sizes and sauces.
public static class SizeType
    {
        public static  string Big { get { return "Big";}}
        public static string Small { get { return "Small";}}
    }
    
    public static class SauceType
    {
        public static string Sweet { get { return "Sweet Sauce"; } }
        public static string Hot { get { return "Hot Sauce"; } }
        public static string Italian { get { return "Italian Sauce"; } }
    }
Step 6: As per Director definition on top, it is responsible for managing the correct sequence of object creation. It receives a Concrete Builder as a parameter and executes the necessary operations on it.
But, we created in such a way that there is no job left for Director. Lets move to main program so called client code.
Step 7: Put the required code in Main() method.
class Program
    {
        static void Main(string[] args)
        {
            NewPizza pizza = new NewPizza(new AmericanBuilder()._builder);
            pizza.DisplayOrder();
            
            pizza = new NewPizza(new MexicanBuilder()._builder);
            pizza.DisplayOrder();
            Console.ReadKey();
        }
    }

Out Put:
Basic rule of Patterns : It is not impossible to implement the same solution using other pattern. But, this is the preferred way to do so.
We will see better solutions as we move on to remaining patterns.
Happy coding. :)

Is it helpful for you? Kindly let me know your comments / Questions.

3 comments:

  1. Hey Pratap, Very good example! The only point where I beg to differ is this:
    public Builder _builder;
    I wouldn't want to expose this out, there doesn't seem to be any benefit in doing so. Having to return a _builder also wouldn't make sense for the Isxxx() functions - your thoughts?

    - Gopa

    ReplyDelete
  2. Gopa
    Good Point. I accept with ur first point. But i intentionally made the return type as _Builder, just to provide the chaining of methods like
    Obj.MethodA().MethodB().MethodC();
    instead of
    Obj.MethodA();
    Obj.MethodB();
    Obj.MethodC();

    ReplyDelete
  3. I think there should be one abstract class or interface as per diagram.
    nice example to work with.

    ReplyDelete