Builder Design Pattern
June 11, 2024 ยท View on GitHub
The builder pattern is a design pattern designed to provide a flexible solution to various object creation problems in object-oriented programming. The intent of the builder design pattern is to separate the construction of a complex object from its representation. It is one of the Gang of Four design patterns.
Resources:
Design Patterns for Unit Testing - Builder Pattern by Infallible Code
Example:
Tip
Just attach Program script to a object in the scene and play.
Program Class:
using UnityEngine;
namespace Builder
{
/// <summary>
/// The main entry point for the Unity application.
/// </summary>
public class Program : MonoBehaviour
{
/// <summary>
/// Unity's Start method. This method is called when the script instance is being loaded.
/// </summary>
private void Start()
{
// Create a new Director instance
var director = new Director();
// Create a new ConcreteBuilder instance
var builder = new ConcreteBuilder();
// Assign the builder to the director
director.Builder = builder;
// Build a minimal viable product
print("basic:");
director.BuildMinimalViableProduct();
print(builder.GetProduct().ListParts());
// Build a full-featured product
print("Full:");
director.BuildFullFeaturedProduct();
print(builder.GetProduct().ListParts());
// Build a custom product
print("Custom:");
builder.BuildPartA();
builder.BuildPartB();
print(builder.GetProduct().ListParts());
}
}
}
Director Class:
The Director class doesn't create and assemble objects directly. Instead, the Director refers to the Builder interface for building (creating and assembling) the parts of a complex object, which makes the Director independent of which concrete classes are instantiated (which representation is created).
namespace Builder
{
/// <summary>
/// The Director class is responsible for constructing products using a builder object.
/// </summary>
public class Director
{
private IBuilder _builder;
/// <summary>
/// Sets the builder object to be used for constructing products.
/// </summary>
/// <param name="value">The builder object.</param>
public IBuilder Builder
{
set => _builder = value;
}
/// <summary>
/// Constructs a minimal viable product using the builder object.
/// </summary>
public void BuildMinimalViableProduct()
{
_builder.BuildPartA();
}
/// <summary>
/// Constructs a full-featured product using the builder object.
/// </summary>
public void BuildFullFeaturedProduct()
{
_builder.BuildPartA();
_builder.BuildPartB();
_builder.BuildPartC();
}
}
}
IBuilder Interface:
Abstract interface for creating objects (product).
namespace Builder
{
public interface IBuilder
{
void BuildPartA();
void BuildPartB();
void BuildPartC();
}
}
ConcerteBuilder Class:
Provides implementation for Builder. It is an object able to construct other objects. Constructs and assembles parts to build the objects.
namespace Builder
{
/// <summary>
/// ConcreteBuilder class implements the IBuilder interface and constructs a complex object using a step-by-step approach.
/// </summary>
public class ConcreteBuilder : IBuilder
{
private Product _product = new Product();
/// <summary>
/// Initializes a new instance of the <see cref="ConcreteBuilder"/> class.
/// Resets the internal state of the builder.
/// </summary>
public ConcreteBuilder()
{
Reset();
}
/// <summary>
/// Resets the internal state of the builder.
/// </summary>
private void Reset()
{
_product = new Product();
}
/// <summary>
/// Adds a part A to the product being constructed.
/// </summary>
public void BuildPartA()
{
_product.Add("PartA1");
}
/// <summary>
/// Adds a part B to the product being constructed.
/// </summary>
public void BuildPartB()
{
_product.Add("PartB1");
}
/// <summary>
/// Adds a part C to the product being constructed.
/// </summary>
public void BuildPartC()
{
_product.Add("PartC1");
}
/// <summary>
/// Retrieves the constructed product and resets the internal state of the builder.
/// </summary>
/// <returns>The constructed product.</returns>
public Product GetProduct()
{
var result = _product;
Reset();
return result;
}
}
}
Product Class:
using System.Collections.Generic;
namespace Builder
{
/// <summary>
/// Represents a product with its parts.
/// </summary>
public class Product
{
/// <summary>
/// Holds the parts of the product.
/// </summary>
private readonly List<object> _parts = new();
/// <summary>
/// Adds a part to the product.
/// </summary>
/// <param name="part">The part to be added.</param>
public void Add(string part)
{
_parts.Add(part);
}
/// <summary>
/// Lists all parts of the product.
/// </summary>
/// <returns>A string representation of the product's parts.</returns>
public string ListParts()
{
var str = string.Empty;
for (var i = 0; i < _parts.Count; i++)
{
str += _parts[i] + ", ";
}
str = str.Remove(str.Length - 2);
return "Product parts: " + str + "\n";
}
}
}
.NET Optimized code in C#:
The .NET optimized code uses more modern C# and .NET features.
namespace Builder.NetOptimized;
using static System.Console;
/// <summary>
/// Builder Design Pattern
/// </summary>
public class Program
{
public static void Main()
{
// Create shop
var shop = new Shop();
// Construct and display vehicles
shop.Construct(new ScooterBuilder());
shop.ShowVehicle();
shop.Construct(new CarBuilder());
shop.ShowVehicle();
shop.Construct(new MotorCycleBuilder());
shop.ShowVehicle();
// Wait for user
ReadKey();
}
}
/// <summary>
/// The 'Director' class
/// </summary>
public class Shop
{
private VehicleBuilder? builder;
// Builder uses a complex series of steps
public void Construct(VehicleBuilder vehicleBuilder)
{
builder = vehicleBuilder;
builder.BuildFrame();
builder.BuildEngine();
builder.BuildWheels();
builder.BuildDoors();
}
public void ShowVehicle()
{
builder?.Vehicle.Show();
}
}
/// <summary>
/// The 'Builder' abstract class
/// </summary>
public abstract class VehicleBuilder(VehicleType vehicleType)
{
public Vehicle Vehicle { get; private set; } = new Vehicle(vehicleType);
public abstract void BuildFrame();
public abstract void BuildEngine();
public abstract void BuildWheels();
public abstract void BuildDoors();
}
/// <summary>
/// The 'ConcreteBuilder1' class
/// </summary>
public class MotorCycleBuilder : VehicleBuilder
{
// Invoke base class constructor
public MotorCycleBuilder() : base(VehicleType.MotorCycle)
{
}
public override void BuildFrame() => Vehicle[PartType.Frame] = "MotorCycle Frame";
public override void BuildEngine() => Vehicle[PartType.Engine] = "500 cc";
public override void BuildWheels() => Vehicle[PartType.Wheel] = "2";
public override void BuildDoors() => Vehicle[PartType.Door] = "0";
}
/// <summary>
/// The 'ConcreteBuilder2' class
/// </summary>
public class CarBuilder : VehicleBuilder
{
// Invoke base class constructor
public CarBuilder() : base(VehicleType.Car)
{
}
public override void BuildFrame() => Vehicle[PartType.Frame] = "Car Frame";
public override void BuildEngine() => Vehicle[PartType.Engine] = "2500 cc";
public override void BuildWheels() => Vehicle[PartType.Wheel] = "4";
public override void BuildDoors() => Vehicle[PartType.Door] = "4";
}
/// <summary>
/// The 'ConcreteBuilder3' class
/// </summary>
public class ScooterBuilder : VehicleBuilder
{
// Invoke base class constructor
public ScooterBuilder() : base(VehicleType.Scooter)
{
}
public override void BuildFrame() => Vehicle[PartType.Frame] = "Scooter Frame";
public override void BuildEngine() => Vehicle[PartType.Engine] = "50 cc";
public override void BuildWheels() => Vehicle[PartType.Wheel] = "2";
public override void BuildDoors() => Vehicle[PartType.Door] = "0";
}
/// <summary>
/// The 'Product' class
/// </summary>
public class Vehicle(VehicleType vehicleType)
{
private readonly Dictionary<PartType, string> parts = [];
private readonly VehicleType vehicleType = vehicleType;
public string this[PartType key]
{
get => parts[key];
set => parts[key] = value;
}
public void Show()
{
WriteLine("\n---------------------------");
WriteLine($"Vehicle Type: {vehicleType}");
WriteLine($" Frame : {this[PartType.Frame]}");
WriteLine($" Engine : {this[PartType.Engine]}");
WriteLine($" #Wheels: {this[PartType.Wheel]}");
WriteLine($" #Doors : {this[PartType.Door]}");
}
}
/// <summary>
/// Part type enumeration
/// </summary>
public enum PartType
{
Frame,
Engine,
Wheel,
Door
}
/// <summary>
/// Vehicle type enumeration
/// </summary>
public enum VehicleType
{
Car,
Scooter,
MotorCycle
}