Builder Design Pattern (Creational)
Builder is a creational design pattern that lets you construct complex objects step by step. The pattern allows you to produce different types and representations of an object using the same construction code.
Problem
Section titled βProblemβImagine a complex object that requires laborious, step-by-step initialization of many fields and nested objects. Such initialization code is usually buried inside a monstrous constructor with dozens of parameters. Or even worse: scattered all over the client code.
For example, letβs think about how to create a House object. To build a simple house, you need to construct four walls and a floor, install a door, fit a pair of windows, and build a roof. But what if you want a larger, brighter house, with a heating system, plumbing, and electrical wiring?
The simplest solution is to extend the base House class and create a set of subclasses to cover all combinations of the parameters. But eventually, youβll end up with a considerable number of subclasses. Any new parameter, such as the porch style, will require growing this hierarchy even more.
Solution
Section titled βSolutionβThe Builder pattern suggests that you extract the object construction code out of its own class and move it to separate objects called builders.
The pattern organizes object construction into a set of steps (buildWalls, buildDoor, etc.). To create an object, you execute a series of these steps on a builder object. The important part is that you donβt need to call all of the steps. You can call only those steps that are necessary for producing a particular configuration of an object.
Some of the construction steps might require different implementations when you need to build various representations of the product. For example, walls of a cabin may be built of wood, but the castle walls must be built with stone.
In this case, you can create several different builder classes that implement the same set of building steps, but in a different manner. You can then use these builders with the same construction process (the Director) to produce different kinds of objects.
Real-World Analogy
Section titled βReal-World AnalogyβThink of a fast-food restaurant. The process of making a βValue Mealβ is a builder process.
- The Director is the cashier who takes your order and tells the kitchen what to make.
- The Builder is the kitchen staff.
- The Steps are:
addMainItem(),addSideItem(),addDrink(). - The Product is your meal.
Depending on whether you ordered a βKids Mealβ or a βSuper Size Meal,β the builder uses the same steps but different components (small burger vs. big burger).
Structure (Class Diagram)
Section titled βStructure (Class Diagram)βclassDiagram
class Director {
- builder: Builder
+ construct()
}
class Builder {
<<interface>>
+ reset()
+ buildStepA()
+ buildStepB()
+ buildStepZ()
}
class ConcreteBuilder {
- result: Product
+ reset()
+ buildStepA()
+ buildStepB()
+ buildStepZ()
+ getResult() Product
}
class Product {
}
Director o-- Builder
Builder <|.. ConcreteBuilder
ConcreteBuilder ..> Product : creates
Pseudocode
Section titled βPseudocodeβ# The builder interface specifies methods for creating the# different parts of the product objects.interface Builder: method reset() method buildWalls() method buildDoors() method buildWindows() method buildRoof() method buildGarage()
# Concrete builders provide different implementations of the# construction steps.class HouseBuilder implements Builder: private product: House method reset(): this.product = new House() method buildWalls(): # Build brick walls. method buildDoors(): # Build wooden door. # ... (other methods) method getResult(): House: return this.product
# The Director is only responsible for executing the building# steps in a particular sequence.class Director: method makeSUV(builder: Builder): builder.reset() builder.buildWalls() builder.buildDoors() builder.buildRoof()Applicability
Section titled βApplicabilityβ- Use the Builder pattern to get rid of a βtelescoping constructor.β (A constructor with 10 optional parameters).
- Use the Builder pattern when you want your code to be able to create different representations of some product (for example, stone and wooden houses).
- Use the Builder to construct Composite trees or other complex objects.
How to Implement
Section titled βHow to Implementβ- Clearly define the common construction steps for building all available product representations.
- Declare these steps in the base builder interface.
- Create a concrete builder class for each of the product representations and implement their construction steps.
- Think about creating a director class. It may encapsulate various ways to construct a product using the same builder object.
- The client code creates both the builder and the director.
Pros and Cons
Section titled βPros and ConsβPros:
- You can construct objects step-by-step, defer construction steps or run steps recursively.
- You can reuse the same construction code when building various representations of products.
- Single Responsibility Principle. You isolate complex construction code from the business logic of the product.
Cons:
- The overall complexity of the code increases since the pattern requires creating multiple new classes.
Relations with other Design Patterns
Section titled βRelations with other Design Patternsβ- Many designs start by using Factory Method and evolve toward Abstract Factory, Prototype, or Builder.
- Builder focuses on constructing complex objects step by step. Abstract Factory specializes in creating families of related objects.
- You can use Builder when creating complex Composite trees because you can program its construction steps to work recursively.
Code Examples
Section titled βCode ExamplesβCode Examples (C#)
Section titled βCode Examples (C#)β// Productpublic class Meal{ public string Main { get; set; } public string Drink { get; set; }
public override string ToString() => $"{Main} + {Drink}";}
// Builderpublic interface IMealBuilder{ void BuildMain(); void BuildDrink(); Meal GetMeal();}
// Concrete Builderpublic class KidsMealBuilder : IMealBuilder{ private Meal _meal = new Meal();
public void BuildMain() => _meal.Main = "Hamburger"; public void BuildDrink() => _meal.Drink = "Juice"; public Meal GetMeal() => _meal;}
// Directorpublic class MealDirector{ public Meal Construct(IMealBuilder builder) { builder.BuildMain(); builder.BuildDrink(); return builder.GetMeal(); }}
// Usageclass Program{ static void Main() { var director = new MealDirector(); var builder = new KidsMealBuilder();
var meal = director.Construct(builder); Console.WriteLine(meal); }}Code Examples (Python)
Section titled βCode Examples (Python)βfrom abc import ABC, abstractmethod
class Builder(ABC): @property @abstractmethod def product(self): pass
@abstractmethod def produce_part_a(self): pass
@abstractmethod def produce_part_b(self): pass
class ConcreteBuilder1(Builder): def __init__(self): self.reset()
def reset(self): self._product = Product1()
@property def product(self): product = self._product self.reset() return product
def produce_part_a(self): self._product.add("PartA1")
def produce_part_b(self): self._product.add("PartB1")
class Product1(): def __init__(self): self.parts = []
def add(self, part): self.parts.append(part)
def list_parts(self): print(f"Product parts: {', '.join(self.parts)}", end="")
class Director: def __init__(self): self._builder = None
@property def builder(self): return self._builder
@builder.setter def builder(self, builder): self._builder = builder
def build_minimal_viable_product(self): self.builder.produce_part_a()
def build_full_featured_product(self): self.builder.produce_part_a() self.builder.produce_part_b()
if __name__ == "__main__": director = Director() builder = ConcreteBuilder1() director.builder = builder
print("Standard basic product:") director.build_minimal_viable_product() builder.product.list_parts()
print("\\n\\nStandard full featured product:") director.build_full_featured_product() builder.product.list_parts()Code Examples (C++)
Section titled βCode Examples (C++)β#include <iostream>#include <memory>#include <string>
// ============================// Product// ============================class Meal {public: std::string Main; std::string Drink;
std::string ToString() const { return Main + " + " + Drink; }};
// ============================// Builder// ============================class IMealBuilder {public: virtual ~IMealBuilder() = default;
virtual void BuildMain() = 0; virtual void BuildDrink() = 0; virtual std::unique_ptr<Meal> GetMeal() = 0;};
// ============================// Concrete Builder// ============================class KidsMealBuilder : public IMealBuilder {private: std::unique_ptr<Meal> meal;
public: KidsMealBuilder() : meal(std::make_unique<Meal>()) {}
void BuildMain() override { meal->Main = "Hamburger"; }
void BuildDrink() override { meal->Drink = "Juice"; }
std::unique_ptr<Meal> GetMeal() override { return std::move(meal); }};
// ============================// Director// ============================class MealDirector {public: std::unique_ptr<Meal> Construct(IMealBuilder& builder) { builder.BuildMain(); builder.BuildDrink(); return builder.GetMeal(); }};
// ============================// Usage// ============================int main() { MealDirector director; KidsMealBuilder builder;
auto meal = director.Construct(builder); std::cout << meal->ToString() << std::endl;
return 0;}Code Examples (Java)
Section titled βCode Examples (Java)β// Productpublic class Meal { private String main; private String drink;
public String getMain() { return main; }
public void setMain(String main) { this.main = main; }
public String getDrink() { return drink; }
public void setDrink(String drink) { this.drink = drink; }
@Override public String toString() { return main + " + " + drink; }}
// Builder Interfacepublic interface IMealBuilder { void buildMain(); void buildDrink(); Meal getMeal();}
// Concrete Builderpublic class KidsMealBuilder implements IMealBuilder {
private Meal meal = new Meal();
@Override public void buildMain() { meal.setMain("Hamburger"); }
@Override public void buildDrink() { meal.setDrink("Juice"); }
@Override public Meal getMeal() { return meal; }}
// Directorpublic class MealDirector {
public Meal construct(IMealBuilder builder) { builder.buildMain(); builder.buildDrink(); return builder.getMeal(); }}
// Usagepublic class Program { public static void main(String[] args) { MealDirector director = new MealDirector(); IMealBuilder builder = new KidsMealBuilder();
Meal meal = director.construct(builder); System.out.println(meal); }}Code Examples (JavaScript)
Section titled βCode Examples (JavaScript)β// Productclass Meal { constructor() { this.main = ""; this.drink = ""; }
toString() { return `${this.main} + ${this.drink}`; }}
// Builder βInterfaceβclass IMealBuilder { buildMain() { throw new Error("buildMain() must be implemented"); }
buildDrink() { throw new Error("buildDrink() must be implemented"); }
getMeal() { throw new Error("getMeal() must be implemented"); }}
// Concrete Builderclass KidsMealBuilder extends IMealBuilder { constructor() { super(); this.meal = new Meal(); }
buildMain() { this.meal.main = "Hamburger"; }
buildDrink() { this.meal.drink = "Juice"; }
getMeal() { return this.meal; }}
// Directorclass MealDirector { construct(builder) { builder.buildMain(); builder.buildDrink(); return builder.getMeal(); }}
// Usageconst director = new MealDirector();const builder = new KidsMealBuilder();
const meal = director.construct(builder);console.log(meal.toString());Code Examples (Visual Basic)
Section titled βCode Examples (Visual Basic)β' ============================' Product' ============================Public Class Meal Public Property Main As String Public Property Drink As String
Public Overrides Function ToString() As String Return $"{Main} + {Drink}" End FunctionEnd Class
' ============================' Builder' ============================Public Interface IMealBuilder Sub BuildMain() Sub BuildDrink() Function GetMeal() As MealEnd Interface
' ============================' Concrete Builder' ============================Public Class KidsMealBuilder Implements IMealBuilder
Private _meal As New Meal()
Public Sub BuildMain() Implements IMealBuilder.BuildMain _meal.Main = "Hamburger" End Sub
Public Sub BuildDrink() Implements IMealBuilder.BuildDrink _meal.Drink = "Juice" End Sub
Public Function GetMeal() As Meal Implements IMealBuilder.GetMeal Return _meal End FunctionEnd Class
' ============================' Director' ============================Public Class MealDirector Public Function Construct(builder As IMealBuilder) As Meal builder.BuildMain() builder.BuildDrink() Return builder.GetMeal() End FunctionEnd Class
' ============================' Usage' ============================Module Program Sub Main() Dim director As New MealDirector() Dim builder As New KidsMealBuilder()
Dim meal = director.Construct(builder) Console.WriteLine(meal) End SubEnd ModuleCode Examples (Rust)
Section titled βCode Examples (Rust)β// Product#[derive(Debug)]struct Meal { main: String, drink: String,}
impl Meal { fn to_string(&self) -> String { format!("{} + {}", self.main, self.drink) }}
// Buildertrait MealBuilder { fn build_main(&mut self); fn build_drink(&mut self); fn get_meal(&mut self) -> Meal;}
// Concrete Builderstruct KidsMealBuilder { meal: Meal,}
impl KidsMealBuilder { fn new() -> Self { Self { meal: Meal { main: String::new(), drink: String::new(), }, } }}
impl MealBuilder for KidsMealBuilder { fn build_main(&mut self) { self.meal.main = "Hamburger".to_string(); }
fn build_drink(&mut self) { self.meal.drink = "Juice".to_string(); }
fn get_meal(&mut self) -> Meal { Meal { main: self.meal.main.clone(), drink: self.meal.drink.clone(), } }}
// Directorstruct MealDirector;
impl MealDirector { fn construct(&self, builder: &mut dyn MealBuilder) -> Meal { builder.build_main(); builder.build_drink(); builder.get_meal() }}
// Usagefn main() { let director = MealDirector; let mut builder = KidsMealBuilder::new();
let meal = director.construct(&mut builder); println!("{}", meal.to_string());}Code Examples (PHP)
Section titled βCode Examples (PHP)β<?php// Productclass Meal{ public string $main; public string $drink;
public function __toString(): string { return "{$this->main} + {$this->drink}"; }}
// Builderinterface IMealBuilder{ public function buildMain(): void; public function buildDrink(): void; public function getMeal(): Meal;}
// Concrete Builderclass KidsMealBuilder implements IMealBuilder{ private Meal $meal;
public function __construct() { $this->meal = new Meal(); }
public function buildMain(): void { $this->meal->main = "Hamburger"; }
public function buildDrink(): void { $this->meal->drink = "Juice"; }
public function getMeal(): Meal { return $this->meal; }}
// Directorclass MealDirector{ public function construct(IMealBuilder $builder): Meal { $builder->buildMain(); $builder->buildDrink(); return $builder->getMeal(); }}
// Usage$director = new MealDirector();$builder = new KidsMealBuilder();
$meal = $director->construct($builder);echo $meal . "\n";Code Examples (Go)
Section titled βCode Examples (Go)βpackage main
import ( "fmt")
// Producttype Meal struct { Main string Drink string}
func (m Meal) String() string { return fmt.Sprintf("%s + %s", m.Main, m.Drink)}
// Builder interfacetype MealBuilder interface { BuildMain() BuildDrink() GetMeal() Meal}
// Concrete Buildertype KidsMealBuilder struct { meal Meal}
func NewKidsMealBuilder() *KidsMealBuilder { return &KidsMealBuilder{ meal: Meal{}, }}
func (b *KidsMealBuilder) BuildMain() { b.meal.Main = "Hamburger"}
func (b *KidsMealBuilder) BuildDrink() { b.meal.Drink = "Juice"}
func (b *KidsMealBuilder) GetMeal() Meal { return b.meal}
// Directortype MealDirector struct{}
func (d MealDirector) Construct(builder MealBuilder) Meal { builder.BuildMain() builder.BuildDrink() return builder.GetMeal()}
// Usagefunc main() { director := MealDirector{} builder := NewKidsMealBuilder()
meal := director.Construct(builder) fmt.Println(meal)}Code Examples (Ruby)
Section titled βCode Examples (Ruby)β# Productclass Mealattr_accessor :main, :drink
def to_s "#{main} + #{drink}"endend
# Builderclass MealBuilderdef build_main raise NotImplementedError, "Subclasses must implement build_main"end
def build_drink raise NotImplementedError, "Subclasses must implement build_drink"end
def get_meal raise NotImplementedError, "Subclasses must implement get_meal"endend
# Concrete Builderclass KidsMealBuilder < MealBuilderdef initialize @meal = Meal.newend
def build_main @meal.main = "Hamburger"end
def build_drink @meal.drink = "Juice"end
def get_meal @mealendend
# Directorclass MealDirectordef construct(builder) builder.build_main builder.build_drink builder.get_mealendend
# Usagedirector = MealDirector.newbuilder = KidsMealBuilder.new
meal = director.construct(builder)puts mealCode Examples (Perl)
Section titled βCode Examples (Perl)β#!/usr/bin/perluse strict;use warnings;
############################# Product############################package Meal;
sub new { my ($class) = @_; return bless { main => undef, drink => undef }, $class;}
sub set_main { $_[0]->{main} = $_[1]; }sub set_drink { $_[0]->{drink} = $_[1]; }
sub to_string { my ($self) = @_; return "$self->{main} + $self->{drink}";}
############################# Builder (Interface-like)############################package MealBuilder;
sub build_main { die "build_main() must be implemented by subclass"; }sub build_drink { die "build_drink() must be implemented by subclass"; }sub get_meal { die "get_meal() must be implemented by subclass"; }
############################# Concrete Builder############################package KidsMealBuilder;our @ISA = ('MealBuilder');
sub new { my ($class) = @_; return bless { meal => Meal->new() }, $class;}
sub build_main { my ($self) = @_; $self->{meal}->set_main("Hamburger");}
sub build_drink { my ($self) = @_; $self->{meal}->set_drink("Juice");}
sub get_meal { my ($self) = @_; return $self->{meal};}
############################# Director############################package MealDirector;
sub new { bless {}, shift }
sub construct { my ($self, $builder) = @_; $builder->build_main(); $builder->build_drink(); return $builder->get_meal();}
############################# Usage############################package main;
my $director = MealDirector->new();my $builder = KidsMealBuilder->new();
my $meal = $director->construct($builder);print $meal->to_string(), "\n";Code Examples (TypeScript)
Section titled βCode Examples (TypeScript)β// Productclass Meal { main: string = ""; drink: string = "";
toString(): string { return `${this.main} + ${this.drink}`; }}
// Builder Interfaceinterface IMealBuilder { buildMain(): void; buildDrink(): void; getMeal(): Meal;}
// Concrete Builderclass KidsMealBuilder implements IMealBuilder { private meal: Meal = new Meal();
buildMain(): void { this.meal.main = "Hamburger"; }
buildDrink(): void { this.meal.drink = "Juice"; }
getMeal(): Meal { return this.meal; }}
// Directorclass MealDirector { construct(builder: IMealBuilder): Meal { builder.buildMain(); builder.buildDrink(); return builder.getMeal(); }}
// Usageconst director = new MealDirector();const builder = new KidsMealBuilder();
const meal = director.construct(builder);console.log(meal.toString());