Builder Pattern
The Builder is a creational design pattern that separates the construction of a complex object from its representation, allowing the same construction process to create different representations. It is ideal when an object has many optional parameters or configuration steps, avoiding telescoping constructors.
Key Principles
- Step-by-step construction: Build object in phases (e.g., required → optional features).
- Fluent interface: Chainable methods (
.set_x().set_y()) for readability. - Director (optional): Orchestrates building process; Builder handles details.
- Immutability: Final product often immutable.
Structure
- Product: The complex object being built.
- Builder: Interface/abstract class with methods for each construction step.
- Concrete Builder: Implements steps and returns final product.
- Director (optional): Uses builder to construct specific variants.
Python Example: Computer Builder
from abc import ABC, abstractmethod
from typing import Optional
# Product
class Computer:
def __init__(self):
self.cpu: Optional[str] = None
self.ram: Optional[int] = None
self.storage: Optional[int] = None
self.gpu: Optional[str] = None
self.os: Optional[str] = None
def __str__(self):
return f"Computer [CPU={self.cpu}, RAM={self.ram}GB, Storage={self.storage}GB, GPU={self.gpu}, OS={self.os}]"
# Abstract Builder
class ComputerBuilder(ABC):
@abstractmethod
def set_cpu(self) -> 'ComputerBuilder':
pass
@abstractmethod
def set_ram(self) -> 'ComputerBuilder':
pass
@abstractmethod
def set_storage(self) -> 'ComputerBuilder':
pass
@abstractmethod
def set_gpu(self) -> 'ComputerBuilder':
pass
@abstractmethod
def set_os(self) -> 'ComputerBuilder':
pass
@abstractmethod
def build(self) -> Computer:
pass
# Concrete Builder
class GamingComputerBuilder(ComputerBuilder):
def __init__(self):
self.computer = Computer()
def set_cpu(self) -> 'GamingComputerBuilder':
self.computer.cpu = "Intel i9-13900K"
return self
def set_ram(self) -> 'GamingComputerBuilder':
self.computer.ram = 64
return self
def set_storage(self) -> 'GamingComputerBuilder':
self.computer.storage = 2000 # 2TB SSD
return self
def set_gpu(self) -> 'GamingComputerBuilder':
self.computer.gpu = "NVIDIA RTX 4090"
return self
def set_os(self) -> 'GamingComputerBuilder':
self.computer.os = "Windows 11"
return self
def build(self) -> Computer:
return self.computer
# Optional Director
class ComputerDirector:
def construct_gaming_pc(self, builder: ComputerBuilder) -> Computer:
return (builder
.set_cpu()
.set_ram()
.set_storage()
.set_gpu()
.set_os()
.build())
# Usage (Client Code)
if __name__ == "__main__":
# With Director
director = ComputerDirector()
gaming_pc = director.construct_gaming_pc(GamingComputerBuilder())
print(gaming_pc)
# Direct fluent usage
budget_pc = (GamingComputerBuilder()
.set_cpu() # Reuse same builder logic or create new
.set_ram() # Override for budget
.set_ram().ram = 16 # Custom tweak
.set_storage().storage = 512
.set_gpu().gpu = "Integrated"
.set_os().os = "Linux"
.build())
print(budget_pc)
Output:
Computer [CPU=Intel i9-13900K, RAM=64GB, Storage=2000GB, GPU=NVIDIA RTX 4090, OS=Windows 11]
Computer [CPU=Intel i9-13900K, RAM=16GB, Storage=512GB, GPU=Integrated, OS=Linux]
When to Use Builder
- Object has many optional parameters (avoid long constructors).
- Need different representations from same steps.
- Want readable, fluent construction (e.g., SQL query builders).
Summary: Builder lets you construct complex objects step-by-step with a fluent interface, keeping construction logic separate from the product.