The first pattern in Creational Design Patterns is Builder which allows you to construct complex objects piecewise. Let's get to know more about this design pattern.
Note: Code can be downloaded at my Github
1. What is the Builder Pattern?
The Builder Design Pattern helps to separate the construction from the representation of a complex object. Therefore, by utilizing the same construction process, the program can create different representations.
Builder is a creational design pattern that lets you construct complex object step by step. The pattern allows you to produce different types and representations of an object using the same construction code.
2. When to Implement the Builder Pattern?
As the definition suggests, when you need to create a complex object which has different representations, you would need to implement a solid Builder Design Pattern.
Let's take an example of building HTML string without implementing Builder pattern. Create a Console Application in Visual Studio and name it BuilderDemo. In Program.cs, we have below code.
class Program { static void Main(string[] args) { var hello = "hello"; var sb = new StringBuilder(); sb.Append("<p>"); sb.Append(hello); sb.Append("</p>"); Console.WriteLine(sb); sb.Clear(); var words = new[] { "hello", "world" }; sb.Append("<ul>"); foreach(var word in words) { sb.AppendFormat("<li>{0}</li>", word); } sb.Append("</ul>"); Console.WriteLine(sb); } }
The code looks fine and is able to produce the HTML strings that we need. However, for each representation, the program requires different creational implementations to construct the results. Here, we can see more works need to be done for the unordered list element (<ul></ul>) than the paragraph element (<p></p>).
3. Example of the Builder Design Pattern
HTML is a complex object and it has different representations. So, we need to implement a central mechanism which is able to produce different HTML elements. That central mechanism where we implement the Builder Design Pattern.
Step 1: Create an HTMLElement class
This class is for initiating html element object which hold the information for each element and its child elements.
public class HTMLElement { public string Name { get; set; } public string Text { get; set; } public List<HTMLElement> Elements = new List<HTMLElement>(); }
Step 2: Create an HTMLBuilder class
This class is a builder which is used to construct the HTML string by defining and adding elements.
public class HTMLBuilder { private const int indentSize = 2; private HTMLElement rootElement; public HTMLBuilder(string rootName) { rootElement = new HTMLElement(); rootElement.Name = rootName; } public HTMLBuilder AddChild(string name, string text) { rootElement.Elements.Add(new HTMLElement() { Name = name, Text = text }); return this; } public string ToStringImpl(HTMLElement root, int indent) { var sb = new StringBuilder(); var i = new string(' ', indentSize * indent); if(!string.IsNullOrEmpty(root.Name)) sb.AppendLine($"{i}<{root.Name}>"); if (!string.IsNullOrEmpty(root.Text)) { sb.Append(new string(' ', indentSize * (indent + 1))); sb.AppendLine(root.Text); } foreach (var e in root.Elements) { sb.Append(ToStringImpl(e, string.IsNullOrEmpty(root.Name) ? indent : indent + 1)); } if (!string.IsNullOrEmpty(root.Name)) sb.AppendLine($"{i}</{root.Name}>"); return sb.ToString(); } public override string ToString() { return ToStringImpl(rootElement, 0); } }
So from now, if you want to create an HTML string, just need to initiate the builder and add elements by calling AddChild function. It's the same way of create the 2 objects but result in 2 different presentations.
class Program { static void Main(string[] args) { var pBuilder = new HTMLBuilder(""); pBuilder.AddChild("p", "hello world"); Console.WriteLine(pBuilder.ToString()); var ulBuilder = new HTMLBuilder("ul"); ulBuilder.AddChild("li", "hello").AddChild("li", "world"); Console.WriteLine(ulBuilder.ToString()); } }
4. Functional Builder
We can enhance the Builder Pattern to be better in a more functional way. If you read my post about The S.O.L.I.D Design Principles, you will see the Functional Builder implementation follows the Open-Closed principle which is open for extension but closed for modification.
Let's take a look at an example of a PersonBuilder below.
public class Person { public string Name, Position; } public abstract class FunctionalBuilder<TSubject, TSelf> where TSelf : FunctionalBuilder<TSubject, TSelf> where TSubject: new() { private readonly List<Func<TSubject, TSubject>> actions = new List<Func<TSubject, TSubject>>(); public TSelf Do(Action<TSubject> action) => AddAction(action); public TSubject Build() => actions.Aggregate(new TSubject(), (p, f) => f(p)); private TSelf AddAction(Action<TSubject> action) { actions.Add(p => { action(p); return p; }); return (TSelf)this; } } public sealed class PersonBuilder : FunctionalBuilder<Person, PersonBuilder> { public PersonBuilder Called(string name) => Do(p => p.Name = name); } public static class PersonBuilderExtensions { public static PersonBuilder WorkAs (this PersonBuilder builder, string position) => builder.Do(p => p.Position = position); }
Then, in the main function, you can use the Functional Builder implemented above to initiate a Person object:
class Program { static void Main(string[] args) { var person = new PersonBuilder() .Called("Lucas") .WorkAs("Developer") .Build(); } }
5. Facade Builder
Sometimes, using a single builder to build up a particular object is not enough. In many cases, you want several builders which are responsible for building up several different aspects of an object. This is when a Facade Builder Design Pattern comes in the place.
Take a look at below example of the Facade Builder for Employee class.
public class Employee { public string StreetAddress, PostalCode, City; public string CompanyName, Position; public int AnnualIncome; public override string ToString() { return base.ToString(); } } public class EmployeeBuilder //facade { //reference protected Employee emp = new Employee(); public EmployeeAddressBuilder Lives => new EmployeeAddressBuilder(emp); public EmployeeJobBuilder Works => new EmployeeJobBuilder(emp); } public class EmployeeAddressBuilder : EmployeeBuilder { public EmployeeAddressBuilder(Employee emp) { this.emp = emp; } public EmployeeAddressBuilder At(string streetAddress) { emp.StreetAddress = streetAddress; return this; } public EmployeeAddressBuilder In(string city) { emp.City = city; return this; } public EmployeeAddressBuilder WithPostalCode(string postalCode) { emp.PostalCode = postalCode; return this; } } public class EmployeeJobBuilder : EmployeeBuilder { public EmployeeJobBuilder(Employee emp) { this.emp = emp; } public EmployeeJobBuilder At(string companyName) { emp.CompanyName = companyName; return this; } public EmployeeJobBuilder AsA(string position) { emp.Position = position; return this; } public EmployeeJobBuilder Earning(int amount) { emp.AnnualIncome = amount; return this; } }
In Main function, we can initiate an employee object using the Faceted Builder implemented above.
class Program { static void Main(string[] args) { var employee = new EmployeeBuilder() .Lives.At("Avenue O") .In("Brooklyn") .WithPostalCode("11333") .Works.At("Lucasology") .AsA("Developer") .Earning(100000); } }
We now have a basic idea of implementing Builder Design Pattern from the example above. Let me know your thoughts on this topic and if you have any suggestions or questions, please comment in below this post.