Table of content
- Introduction
- Intent
- Participants
- Example
- Use Cases
Introduction
The Builder Design Pattern is a Creational
design pattern in C# and other object-oriented programming languages.
It is used to construct complex
objects step by step, allowing the construction process to vary independently from the actual representation of the object.
This pattern is particularly useful when you have an object with many possible configurations and it's not desirable to have a large number of constructors with different parameter combinations.
Intent:
- Separate the construction of a
complex object
from its representation. - Allow the same construction process to create different representations.
- Provide a clear and
understandable way to construct an object
, especially when it involves a large number of parameters or configuration options.
Participants:
-
Builder
: An abstract interface that defines the steps and operations needed to construct a product. -
Concrete Builder
: Implementations of the Builder interface, which provide specific methods for building and configuring the product. -
Director
: This class orchestrates the construction process using a builder to create a product. It's not always necessary, but it can be used to ensure that the construction process follows a specific order. -
Product
: The final object that's being constructed, representing the complex object.
Example:
Let's consider an example of building a Pizza object using the Builder pattern. We'll have a Pizza class with various properties like size, crust type, toppings, etc., and a PizzaBuilder to create Pizza objects step by step.
// Product
class Pizza
{
public string Size { get; set; }
public string CrustType { get; set; }
public List<string> Toppings { get; set; }
public void Display()
{
Console.WriteLine($"Size: {Size}, Crust: {CrustType}, Toppings: {string.Join(", ", Toppings)}");
}
}
// Builder
interface IPizzaBuilder
{
void SetSize(string size);
void SetCrustType(string crustType);
void AddTopping(string topping);
Pizza Build();
}
// Concrete Builder
class MargheritaPizzaBuilder : IPizzaBuilder
{
private Pizza pizza = new Pizza();
public void SetSize(string size)
{
pizza.Size = size;
}
public void SetCrustType(string crustType)
{
pizza.CrustType = crustType;
}
public void AddTopping(string topping)
{
pizza.Toppings.Add(topping);
}
public Pizza Build()
{
return pizza;
}
}
// Director (optional)
class PizzaDirector
{
private IPizzaBuilder builder;
public PizzaDirector(IPizzaBuilder builder)
{
this.builder = builder;
}
public Pizza Construct()
{
builder.SetSize("Medium");
builder.SetCrustType("Thin");
builder.AddTopping("Cheese");
builder.AddTopping("Tomato Sauce");
return builder.Build();
}
}
Usage
var margheritaBuilder = new MargheritaPizzaBuilder();
var pizzaDirector = new PizzaDirector(margheritaBuilder);
var margheritaPizza = pizzaDirector.Construct();
margheritaPizza.Display();
Output
Size: Medium, Crust: Thin, Toppings: Cheese, Tomato Sauce
Use Cases:
-
Building Complex Objects
: The Builder pattern is useful when you need to create complex objects with multiple configuration options. It provides a more readable and maintainable way to construct such objects. -
Configuring Immutable Objects:
In situations where you want to create immutable objects (objects whose state cannot be changed after creation), the Builder pattern is a good fit. You can build the object step by step and then create an immutable instance. -
Variety of Configurations:
When you have multiple configurations or variations of an object, using a builder can simplify the creation process and improve code readability. -
Fluent Interfaces:
Builders often make use of a fluent interface, which allows you to chain method calls together for a more expressive and intuitive configuration process. -
Unit Testing:
The Builder pattern is helpful in unit testing when you need to create objects for testing purposes. It allows you to create specific instances with different configurations easily.
Top comments (2)
You could try chaining the methods, by having the builder ones return the IPizzaBuilder.
E.g.
public IPizzaBuilder SetSize(string size)
{
pizza.Size = size;
return this;
}
And then you can have something like:
Pizza pizza = builder.AddTopping("Olives").SetCrustType("thin").Build();
Absolutely.