The builder pattern is used to separate logic from data, it's defined as "Separation of the construction of a complex object from its representation so that the same construction process can be used to create multiple representations." What we are aiming for is to reuse common logic to create objects of the same type but different representations.
For example let's look at making a three part dinner, so to me a three part dinner is composed of a protein, a starch and a salad; i basically eat this for dinner almost everyday, but the components of the dinner vary. one day i might do Salmon, spinach, yams, another day i might do chicken breast, caesar salad and buckwheat. the point is that the dinner itself is the same, but the components of the dinner are different.
To get started we are going to need a Dinner class that store all of the properties of our dinner
enum ProtienStyle {raw = 0, bloody = 10, rare = 30, medium = 60, wellDone = 90 }
enum Protien { Chicken= 1, Turkey=2, Salmon=4, Ostrich=8, Squid=16, Bison=32 }
enum Green { Cabbage = 1, Salad = 2, Spinach = 4, Pickles = 8, Cucumber =
16, Peppers = 32, Carrots = 64 }
enum Starch { Yams, Potatos, Buckweat, Bread }
class Dinner
{
public ProtienStyle ProtienStyle { get; set; }
public Protien Protien { get; set; }
public Green Salad { get; set; }
public Starch Starch { get; set; }
public string
Display()
{
var sb = new
StringBuilder();
string protiens = Enum.GetValues(typeof(Protien)).Cast<Protien>()
.Where(p => (p & Protien) ==
p)
.Select(g => g.ToString())
.Aggregate((x, y) => $"{x}, {y}");
sb.AppendLine($"Protien:{protiens} are {ProtienStyle.ToString()}");
sb.AppendLine($"Starch:{Starch.ToString()}");
string ingredients = Enum.GetValues(typeof(Green)).Cast<Green>()
.Where(i => (i & Salad) ==
i)
.Select(g => g.ToString())
.Aggregate((x, y) => $"{x}, {y}");
sb.AppendLine($"Salad:{ingredients}");
return sb.ToString();
}
}
above we define our dinner class, which stores the various components of our dinner next we need to define an abstract dinner builder.
abstract class DinnerBuilder
{
public Dinner Dinner { get; private set; } = new Dinner();
public abstract void
MakeSalad();
public abstract void
CookStarch();
public abstract void
CookMeat();
}
this class defines the abstraction that our concrete implementations are going to have to define, next let's look at our implementations
class SeafoodDinner : DinnerBuilder
{
public SeafoodDinner() => Dinner.ProtienStyle = ProtienStyle.raw;
public override void
CookMeat() => Dinner.Protien = Protien.Salmon |
Protien.Squid;
public override void
CookStarch() => Dinner.Starch = Starch.Yams;
public override void
MakeSalad() => Dinner.Salad = Green.Cabbage;
}
class SurfAndTurfDinner : DinnerBuilder
{
public SurfAndTurfDinner() => Dinner.ProtienStyle =
ProtienStyle.rare;
public override void
CookMeat() => Dinner.Protien = Protien.Bison |
Protien.Squid;
public override void
CookStarch() => Dinner.Starch = Starch.Buckweat;
public override void
MakeSalad() => Dinner.Salad = Green.Salad | Green.Peppers;
}
we can see that this implementations of our Builder class really have no logic, they just hold our data, now our logic is in our DinnerCooker class, this would be referred to as the director.
class DinnerCooker
{
DinnerBuilder _dinnerBuilder;
public DinnerCooker(DinnerBuilder dinnerBuilder)
=> _dinnerBuilder = dinnerBuilder;
public void
CookDinner()
{
_dinnerBuilder.CookStarch();
_dinnerBuilder.CookMeat();
_dinnerBuilder.MakeSalad();
}
public Dinner GetDinner() => _dinnerBuilder.Dinner;
}
in our data cooker, is where the logic sits; the order you'd create your dinner, you'd start with your starch because that takes the longest, then you'd start your meat, and then while those two where cooking you'd make your salad.
now let's take a look at our main
class Program
{
static void Main(string[] args)
{
var dc1 = new DinnerCooker(new SeafoodDinner());
dc1.CookDinner();
Console.WriteLine(dc1.GetDinner().Display());
var dc2 = new
DinnerCooker(new
SurfAndTurfDinner());
dc2.CookDinner();
Console.WriteLine(dc2.GetDinner().Display());
}
}
and as you can see we create our dinnerCooker and pass in our builder to it; the cooker then uses the data in the builder to create our dinner.
using System;
using System.Linq;
using System.Text;
namespace pc.patternBuilder
{
enum ProtienStyle { raw = 0, bloody = 10, rare = 30, medium = 60, wellDone = 90 }
enum Protien { Chicken = 1, Turkey = 2, Salmon = 4, Ostrich = 8, Squid = 16,
Bison = 32 }
enum Green { Cabbage = 1, Salad = 2, Spinach = 4, Pickles = 8, Cucumber =
16, Peppers = 32, Carrots = 64 }
enum Starch { Yams, Potatos, Buckweat, Bread }
class Dinner
{
public ProtienStyle ProtienStyle { get; set; }
public Protien Protien { get; set; }
public Green Salad { get; set; }
public Starch Starch { get; set; }
public string
Display()
{
var sb = new
StringBuilder();
string protiens = Enum.GetValues(typeof(Protien)).Cast<Protien>()
.Where(p => (p &
Protien) == p)
.Select(g => g.ToString())
.Aggregate((x, y) => $"{x}, {y}");
sb.AppendLine($"Protien:{protiens} are {ProtienStyle.ToString()}");
sb.AppendLine($"Starch:{Starch.ToString()}");
string ingredients = Enum.GetValues(typeof(Green)).Cast<Green>()
.Where(i => (i & Salad)
== i)
.Select(g => g.ToString())
.Aggregate((x, y) => $"{x}, {y}");
sb.AppendLine($"Salad:{ingredients}");
return sb.ToString();
}
}
abstract class DinnerBuilder
{
public Dinner Dinner { get; private set; } = new Dinner();
public abstract void
MakeSalad();
public abstract void
CookStarch();
public abstract void
CookMeet();
}
class SeafoodDinner : DinnerBuilder
{
public SeafoodDinner() => Dinner.ProtienStyle = ProtienStyle.raw;
public override void
CookMeet()
=> Dinner.Protien =
Protien.Salmon | Protien.Squid;
public override void
CookStarch()
=> Dinner.Starch = Starch.Yams;
public override void
MakeSalad()
=> Dinner.Salad = Green.Cabbage;
}
class SurfAndTurfDinner : DinnerBuilder
{
public SurfAndTurfDinner() => Dinner.ProtienStyle =
ProtienStyle.rare;
public override void
CookMeet()
=> Dinner.Protien =
Protien.Bison | Protien.Squid;
public override void
CookStarch()
=> Dinner.Starch =
Starch.Buckweat;
public override void
MakeSalad()
=> Dinner.Salad = Green.Salad |
Green.Cucumber | Green.Peppers;
}
class DinnerCooker
{
DinnerBuilder _dinnerBuilder;
public DinnerCooker(DinnerBuilder dinnerBuilder)
=> _dinnerBuilder =
dinnerBuilder;
public void
CookDinner()
{
_dinnerBuilder.CookMeet();
_dinnerBuilder.CookStarch();
_dinnerBuilder.MakeSalad();
}
public Dinner GetDinner() => _dinnerBuilder.Dinner;
}
class Program
{
static void Main(string[] args)
{
var dc1 = new
DinnerCooker(new
SeafoodDinner());
dc1.CookDinner();
Console.WriteLine(dc1.GetDinner().Display());
var dc2 = new
DinnerCooker(new
SurfAndTurfDinner());
dc2.CookDinner();
Console.WriteLine(dc2.GetDinner().Display());
}
}
}