dofactory C# Builder
Participants
#
The classes and objects participating in this pattern include:
- Builder (
VehicleBuilder
)- specifies an abstract interface for creating parts of a Product object
- ConcreteBuilder (
MotorCycleBuilder, CarBuilder, ScooterBuilder
)- constructs and assembles parts of the product by implementing the Builder interface
- defines and keeps track of the representation it creates
- provides an interface for retrieving the product
- Director (
Shop
)- constructs an object using the Builder interface
- Product (
Vehicle
)- represents the complex object under construction. ConcreteBuilder builds the product's internal representation and defines the process by which it's assembled
- includes classes that define the constituent parts, including interfaces for assembling the parts into the final result
The Builder pattern is a creational design pattern that allows for the construction of complex objects step by step. It separates the construction of a complex object from its representation, enabling the same construction process to create different representations.
Why we need it:
-
Complex Object Creation: When an object has a complex construction process with multiple steps, the Builder pattern provides a way to encapsulate this process and hide the complexity from the client.
-
Variability in Object Construction: It allows for the creation of different configurations or representations of the same object, depending on the needs of the application.
-
Immutability: It's particularly useful when you want to create objects that are immutable, meaning their state cannot be changed after they are created.
When to use it:
The Builder pattern is useful in the following situations:
-
Creating Complex Objects: When you have an object that requires a large number of steps or parameters to be properly constructed.
-
Creating Different Configurations: When you need to create objects with different configurations or optional features.
-
Ensuring Immutability: When you want to ensure that the created objects are immutable.
How to use it:
Here's a step-by-step guide on how to use the Builder pattern:
-
Define the Product:
- This is the complex object you want to construct. It should have a set of attributes or properties that need to be set during the construction process.
-
Create the Builder Interface:
- Define an interface that declares the methods for constructing the different parts of the product. Each method corresponds to a step in the construction process.
-
Implement Concrete Builders:
- Create one or more classes that implement the builder interface. Each concrete builder is responsible for constructing a specific version or representation of the product.
-
Create the Director (Optional):
- The director is responsible for orchestrating the construction process. It takes a builder as input and guides it through the steps to build the product. This step is optional, and you can also let the client directly interact with the builder.
-
Client Code:
- The client decides which builder to use and, if a director is present, it instructs the director to use that builder for construction.
Example:
Let's consider an example of building a Computer
object with different components like CPU, RAM, Storage, etc., using the Builder pattern:
// Step 1: Define the Product (Computer)
class Computer
{
public string CPU { get; set; }
public string RAM { get; set; }
public string Storage { get; set; }
// Other properties...
}
// Step 2: Create the Builder Interface
interface IComputerBuilder
{
void SetCPU();
void SetRAM();
void SetStorage();
Computer GetComputer();
}
// Step 3: Implement Concrete Builders
class GamingComputerBuilder : IComputerBuilder
{
private Computer computer = new Computer();
public void SetCPU()
{
computer.CPU = "Gaming CPU";
}
public void SetRAM()
{
computer.RAM = "16GB Gaming RAM";
}
public void SetStorage()
{
computer.Storage = "1TB SSD";
}
public Computer GetComputer()
{
return computer;
}
}
class OfficeComputerBuilder : IComputerBuilder
{
private Computer computer = new Computer();
public void SetCPU()
{
computer.CPU = "Office CPU";
}
public void SetRAM()
{
computer.RAM = "8GB RAM";
}
public void SetStorage()
{
computer.Storage = "500GB HDD";
}
public Computer GetComputer()
{
return computer;
}
}
// Step 4: Create the Director (Optional)
class ComputerEngineer
{
private IComputerBuilder builder;
public ComputerEngineer(IComputerBuilder builder)
{
this.builder = builder;
}
public void ConstructComputer()
{
builder.SetCPU();
builder.SetRAM();
builder.SetStorage();
}
public Computer GetComputer()
{
return builder.GetComputer();
}
}
// Step 5: Client Code
class Program
{
static void Main()
{
IComputerBuilder gamingBuilder = new GamingComputerBuilder();
ComputerEngineer gamingEngineer = new ComputerEngineer(gamingBuilder);
gamingEngineer.ConstructComputer();
Computer gamingComputer = gamingEngineer.GetComputer();
Console.WriteLine($"Gaming Computer: CPU={gamingComputer.CPU}, RAM={gamingComputer.RAM}, Storage={gamingComputer.Storage}");
}
}
In this example:
- We have a
Computer
class representing the product. - We define an interface
IComputerBuilder
with methods to set CPU, RAM, and Storage. - We implement two concrete builders (
GamingComputerBuilder
andOfficeComputerBuilder
) which specify the steps to build different configurations of a computer. - Optionally, we have a
ComputerEngineer
class that guides the construction process. - In the client code, we choose a builder (in this case,
GamingComputerBuilder
), instruct the engineer to construct the computer, and then retrieve the constructed computer.
This example illustrates how the Builder pattern can be used to construct complex objects with different configurations. It provides flexibility and separation between the construction process and the representation of the object.
Relations with Other Patterns guru
-
Many designs start by using Factory Method (less complicated and more customizable via subclasses) and evolve toward Abstract Factory, Prototype, or Builder (more flexible, but more complicated).
-
Builder focuses on constructing complex objects step by step. Abstract Factory specializes in creating families of related objects. Abstract Factory returns the product immediately, whereas Builder lets you run some additional construction steps before fetching the product.
-
You can use Builder when creating complex Composite trees because you can program its construction steps to work recursively.
-
You can combine Builder with Bridge: the director class plays the role of the abstraction, while different builders act as implementations.
-
Abstract Factories, Builders and Prototypes can all be implemented as Singletons.
Rules of thumb sourcemaking
- Sometimes creational patterns are complementary: Builder can use one of the other patterns to implement which components get built. Abstract Factory, Builder, and Prototype can use Singleton in their implementations.
- Builder focuses on constructing a complex object step by step. Abstract Factory emphasizes a family of product objects (either simple or complex). Builder returns the product as a final step, but as far as the Abstract Factory is concerned, the product gets returned immediately.
- Builder often builds a Composite.
- Often, designs start out using Factory Method (less complicated, more customizable, subclasses proliferate) and evolve toward Abstract Factory, Prototype, or Builder (more flexible, more complex) as the designer discovers where more flexibility is needed.