In this article we will talk about the Builder Pattern.
Agenda
- What is Builder Pattern?
- Create McDonald Happy Meal Program.
- Understand the problems with traditional programming style.
- Try some various solutions and there cons.
- See How Builder Pattern solves all problems.
- What are the components involved in the Builder Pattern?
- Create the Code Using the Builder Pattern.
Previous Articles
- Design Patterns: Introduction
- Learn Design Pattern - Singleton Pattern
- Learn Design Pattern - Factory Method Pattern
- Learn Design Pattern - Abstract Factory Pattern
What is Builder Pattern?
- It falls under the creational pattern.
- This pattern is handy when construction of an object is complex (Creating object includes, creating instance + assigning values to some members) and you want to separate the construction logic from the representation so that it can be used with various representations.
- The Factory pattern is more of an answer to: "What is being built?", whereas this pattern is more of an answer to: "How to built it?".
Problem Statement
You all may have heard of McDonald's Happy Meals. Let's create a code for creating a ChickenMcGrill HappyMeal.
A Happy Meal normally consists of 3 things; a burger, a cold drink and a toy.
Step 1
1. Create ChickenMcGrill Class - Burger
public class ChickenMcGrill : IBurger
{
}
2. Create Coke Class - Cold drink
public class Coke : IColdDrink
{
}
3. Create IceAgeToy Class - Toy
public class IceAgeToy : IToy
{
}
Step 2
Create a HappyMeal as:
public class HappyMeal
{
public IBurger Burger
{get; set;}
public IColdDrink ColdDrink
{get; set;}
public IToy Toy
{get; set;}
}
Step 3
Create client code as:
HappyMeal objPriMeal = new HappyMeal();
objPriMeal.Burger = new ChickenMcGrill();
objPriMeal.ColdDrink = new Coke();
objPriMeal.Toy = new IceAgeToy();
Console.WriteLine(objPriMeal);
Problems and various solutions
Problem 1
-
The End client has to be aware of the steps for building the final product.
-
A new Happy meal, say for a McVeggie, results in a substantial amount of cluttered code
(because now ChickenMcGrill will be replaced with McVeggie and Toy with HarryPotterToy).
Solution 1
Change the constructor logic of the Happy Meal as:
public HappyMeal(int type)
{
switch(type)
{
case 1:
this.Burger = new ChickenMcGrill();
this.ColdDrink = new Coke();
this.Toy = new IceAgeToy();
break;
case 2:
this.Burger = new McVeggie();
this.ColdDrink = new Coke();
this.Toy = new HarrPotterToy();
break;
}
}
.
.
.
//ClientCode
HappyMeal objPriMeal = new HappyMeal(1);
Console.WriteLine(objPriMeal);
HappyMeal objPriMeal = new HappyMeal(2);
Console.WriteLine(objPriMeal);
Problem 2
Every new product results in a new case in the constructor.
Solution 2
Factory Method Pattern: Let the Factory create the final Happy Meal Object depending on the scenario.
Problem 3
We can see for every happy meal, the construction process is the same except some attributes are affected and so those representation steps need to be separated from the construction logic.
Solution 3 Builder Pattern
The Builder pattern separates out the representation and creation from each other with the help of Builders and Directors.
Components involved in the Builder pattern are
-
AbstractBuilder - Contains the steps required for creating final concrete object.
-
Builder - Constructs the Individual part of the Concrete Product implementing AbstractBuilder.
-
Director - Construct the complete Concrete Product using Builder.
-
Product - A Complex Object which is required to create.
Code Walkthrough
Step 1
Create the AbstractBuilder with all the necessary steps:
public abstract class AbstractHappyMeal
{
internal HappyMeal happyMeal;
public void CreateHappyMeal()
{
happyMeal = new HappyMeal();
}
public HappyMeal GetHappyMeal()
{
return happyMeal;
}
public abstract void AddBurger();
public abstract void AddColdDrink();
public abstract void AddToy();
}
Step 2
Create a Builder for ChickenMcGrill and McVegie:
public class ChickenMcGrillHappyMeal:AbstractHappyMeal
{
public override void AddBurger()
{
happyMeal.Burger = new ChickenMcGrill();
}
public override void AddColdDrink()
{
happyMeal.ColdDrink = new Coke();
}
public override void AddToy()
{
happyMeal.Toy = new IceAgeToy();
}
}
public class McVeggieHappyMeal:AbstractHappyMeal
{
public override void AddBurger()
{
happyMeal.Burger = new McVeggie();
}
public override void AddColdDrink()
{
happyMeal.ColdDrink = new Coke();
}
public override void AddToy()
{
happyMeal.Toy = new IceAgeToy();
}
}
Step 3
Create Director:
public class Director
{
public HappyMeal GetHappyMeal(AbstractHappyMeal HappyMealBuilder)
{
HappyMealBuilder.CreateHappyMeal();
HappyMealBuilder.AddBurger();
HappyMealBuilder.AddColdDrink();
HappyMealBuilder.AddToy();
return HappyMealBuilder.GetHappyMeal();
}
}
Step 4
No more constructor logic or factory method is required.
Client Code
AbstractHappyMeal objMealBuilder = new McAlooTikkiHappyMeal();
Director objDirector = new Director();
HappyMeal objMeal = objDirector.GetHappyMeal(objMealBuilder);
objMealBuilder = new McVeggieHappyMeal();
objDirector = new Director();
objMeal = objDirector.GetHappyMeal(objMealBuilder);
objMealBuilder = new ChickenMcGrillHappyMeal();
objDirector = new Director();
objMeal = objDirector.GetHappyMeal(objMealBuilder);
Output of the preceding Code
Class Diagram
Note: Patterns are made for solving the business problems. If any pattern does not solve the problem then it would be better you revamp it as per your requirements.
Even in some scenario, 2 or more patterns are used together.
Please download both of the attached source codes for a complete demonstration of the pattern.
The second sample is all about implementing the builder pattern in ASP.Net and you will also find how two patterns (Factory and builder) work together.
Hope you enjoyed reading this article. Keep programming and supply some good comments.
Stay tuned for the next pattern.