Builder
The Builder pattern is a creational design pattern that lets you construct complex objects step-by-step. It allows customization of the object being built without making the construction process itself complex and unmanageable. The pattern separates the construction of a complex object from its representation, so the same construction process can create different representations.
This pattern is useful when an object has multiple optional attributes, or when the construction process is complex and involves many steps. It addresses the problems that can arise when using traditional constructors to create complex objects, particularly telescoping constructors and the need for a separate object for configuration. It promotes code reusability and maintainability by encapsulating the construction logic.
Usage
The Builder pattern is commonly used where:
- Complex Object Creation: When constructing an object requires a sequence of steps and depends on various configuration options.
- Varied Representations: When you need to create different versions or types of an object using the same construction process.
- Avoiding Constructor Complexity: To avoid long and complicated constructors with numerous parameters.
- Immutable Objects: When you want to construct immutable objects, as the builder can assemble the object’s parts before final creation.
Examples
-
Java’s StringBuilder: The
StringBuilderclass in Java effectively implements the Builder pattern. You don’t construct a final string directly; instead, you use methods likeappend(),insert(), anddelete()to build up the string incrementally. Finally,toString()creates the immutableStringobject. This avoids the inefficiencies of repeatedly creating newStringobjects during modification. -
Python’s
datetimemodule: Constructing adatetimeobject in Python can be done directly withdatetime(year, month, day, hour, minute, second). However, thedatetime.datetimeclass also provides a builder-like interface through its various class methods (e.g.,datetime.now(),datetime.fromtimestamp()). These methods allow you to createdatetimeobjects with specific levels of detail, customizing the initialization process. -
Lombok
@BuilderAnnotation (Java): The Lombok library provides the@Builderannotation which generates a builder class for you automatically. This simplifies the use of the Builder pattern substantially and is seen in many Spring Boot projects where complex DTOs are used.
Specimens
15 implementationsThe Builder pattern is a creational design pattern that lets you construct complex objects step-by-step. It allows the same construction process to create different representations of the object. This is useful when an object has many optional parameters or complex dependencies.
The Dart implementation uses a dedicated Builder class with setter methods for each part of the object being built. A separate Director class (optional, but good practice) orchestrates the building process. The build() method in the builder returns the final constructed object. This approach is idiomatic Dart as it leverages classes and methods for structured object creation, and the fluent interface provided by the setters enhances readability. Immutability is encouraged by returning a new instance of the object in the build() method.
// Product class
class Computer {
final String cpu;
final String ram;
final String storage;
final String gpu;
final String monitor;
Computer({
required this.cpu,
required this.ram,
required this.storage,
this.gpu = 'Integrated',
this.monitor = 'None',
});
@override
String toString() {
return 'Computer(cpu: $cpu, ram: $ram, storage: $storage, gpu: $gpu, monitor: $monitor)';
}
}
// Builder class
class ComputerBuilder {
String _cpu = '';
String _ram = '';
String _storage = '';
String _gpu = 'Integrated';
String _monitor = 'None';
ComputerBuilder setCpu(String cpu) {
_cpu = cpu;
return this;
}
ComputerBuilder setRam(String ram) {
_ram = ram;
return this;
}
ComputerBuilder setStorage(String storage) {
_storage = storage;
return this;
}
ComputerBuilder setGpu(String gpu) {
_gpu = gpu;
return this;
}
ComputerBuilder setMonitor(String monitor) {
_monitor = monitor;
return this;
}
Computer build() {
return Computer(
cpu: _cpu,
ram: _ram,
storage: _storage,
gpu: _gpu,
monitor: _monitor,
);
}
}
// Director (optional)
class ComputerDirector {
final ComputerBuilder builder;
ComputerDirector(this.builder);
void constructBasicComputer() {
builder
.setCpu('Intel i5')
.setRam('8GB')
.setStorage('512GB SSD');
}
void constructGamingComputer() {
builder
.setCpu('Intel i7')
.setRam('16GB')
.setStorage('1TB SSD')
.setGpu('Nvidia RTX 3070')
.setMonitor('27" 144Hz');
}
}
void main() {
ComputerDirector director = ComputerDirector(ComputerBuilder());
director.constructBasicComputer();
Computer basicComputer = director.builder.build();
print('Basic Computer: $basicComputer');
director.constructGamingComputer();
Computer gamingComputer = director.builder.build();
print('Gaming Computer: $gamingComputer');
//Directly using the builder
Computer anotherComputer = ComputerBuilder()
.setCpu('AMD Ryzen 5')
.setRam('12GB')
.setStorage('1TB HDD')
.build();
print('Another Computer: $anotherComputer');
}
The Builder pattern separates the construction of a complex object from its representation. It allows creating different variations of an object through a fluent interface, step-by-step, without exposing the complex internal construction logic to the client.
This Scala implementation uses case classes for immutability and a companion object to define the Builder. The Computer class represents the complex object, and the ComputerBuilder provides a fluent API to set its attributes. The build() method constructs the final Computer instance. This approach leverages Scala’s conciseness and immutability features, making the code readable and thread-safe. The use of a companion object is a common Scala practice for creating factory methods and builders.
case class Computer(cpu: String, ram: Int, storage: String, os: String)
object Computer {
class Builder {
private var cpu: String = ""
private var ram: Int = 0
private var storage: String = ""
private var os: String = ""
def withCpu(cpu: String): Builder = {
this.cpu = cpu
this
}
def withRam(ram: Int): Builder = {
this.ram = ram
this
}
def withStorage(storage: String): Builder = {
this.storage = storage
this
}
def withOs(os: String): Builder = {
this.os = os
this
}
def build(): Computer = {
Computer(cpu, ram, storage, os)
}
}
def newBuilder(): Builder = new Builder()
}
object BuilderExample {
def main(args: Array[String]): Unit = {
val gamingComputer = Computer.newBuilder()
.withCpu("Intel i9")
.withRam(32)
.withStorage("1TB SSD")
.withOs("Windows 11")
.build()
val officeComputer = Computer.newBuilder()
.withCpu("Intel i5")
.withRam(16)
.withStorage("512GB SSD")
.withOs("MacOS")
.build()
println(gamingComputer)
println(officeComputer)
}
}
The Builder pattern is a creational design pattern that lets you construct complex objects step by step. The pattern allows for the separation of construction from representation, so the same construction process can create different representations. This is useful when an object has many optional attributes, or when the construction process itself is complex.
The code defines a Report class representing the complex object. A ReportBuilder class provides a fluent interface to set various report sections (title, header, body, footer). The build() method finalizes the report construction. A ReportDirector class orchestrates the building process, potentially using different builders to create different report types. This implementation is idiomatic PHP due to its use of classes, methods, and the fluent interface for configuration, aligning with common object-oriented practices in PHP.
<?php
class Report
{
private $title;
private $header;
private $body;
private $footer;
public function __construct(string $title, string $header, string $body, string $footer)
{
$this->title = $title;
$this->header = $header;
$this->body = $body;
$this->footer = $footer;
}
public function __toString(): string
{
return "Report:\nTitle: {$this->title}\nHeader: {$this->header}\nBody: {$this->body}\nFooter: {$this->footer}\n";
}
}
interface ReportBuilderInterface
{
public function setTitle(string $title): self;
public function setHeader(string $header): self;
public function setBody(string $body): self;
public function setFooter(string $footer): self;
public function build(): Report;
}
class ReportBuilder implements ReportBuilderInterface
{
private $title;
private $header;
private $body;
private $footer;
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
public function setHeader(string $header): self
{
$this->header = $header;
return $this;
}
public function setBody(string $body): self
{
$this->body = $body;
return $this;
}
public function setFooter(string $footer): self
{
$this->footer = $footer;
return $this;
}
public function build(): Report
{
return new Report($this->title, $this->header, $this->body, $this->footer);
}
}
class ReportDirector
{
public function constructSimpleReport(ReportBuilder $builder): Report
{
return $builder->setTitle('Simple Report')
->setHeader('Report Header')
->setBody('Report Body')
->setFooter('Report Footer')
->build();
}
public function constructDetailedReport(ReportBuilder $builder): Report
{
return $builder->setTitle('Detailed Report')
->setHeader('Detailed Report Header with more info')
->setBody('Detailed Report Body with extensive data')
->setFooter('Detailed Report Footer with copyright info')
->build();
}
}
// Usage
$builder = new ReportBuilder();
$director = new ReportDirector();
$simpleReport = $director->constructSimpleReport($builder);
echo $simpleReport . "\n";
$builder = new ReportBuilder(); // Reset builder for a new report
$detailedReport = $director->constructDetailedReport($builder);
echo $detailedReport . "\n";
?>
The Builder pattern allows constructing complex objects step-by-step. It separates the construction process from the object’s representation, enabling different variations of the object to be created using the same construction interface. This is achieved by creating a separate Builder class that defines methods for each step of the object’s construction. The client then uses the builder to construct the object incrementally. This Ruby implementation uses a dedicated ComputerBuilder class to construct a Computer object, demonstrating a clean and flexible approach to object creation. It leverages Ruby’s method chaining and optional parameters for a concise and readable style.
# frozen_string_literal: true
class Computer
attr_reader :cpu, :ram, :storage, :graphics_card, :operating_system
def initialize(cpu: nil, ram: nil, storage: nil, graphics_card: nil, operating_system: nil)
@cpu = cpu
@ram = ram
@storage = storage
@graphics_card = graphics_card
@operating_system = operating_system
end
def to_s
"CPU: #{@cpu}, RAM: #{@ram}, Storage: #{@storage}, Graphics: #{@graphics_card}, OS: #{@operating_system}"
end
end
class ComputerBuilder
def initialize
@computer = Computer.new
end
def with_cpu(cpu)
@computer = @computer.dup # Create a new instance to avoid modifying the original
@computer.instance_variable_set(:@cpu, cpu)
self
end
def with_ram(ram)
@computer = @computer.dup
@computer.instance_variable_set(:@ram, ram)
self
end
def with_storage(storage)
@computer = @computer.dup
@computer.instance_variable_set(:@storage, storage)
self
end
def with_graphics_card(graphics_card)
@computer = @computer.dup
@computer.instance_variable_set(:@graphics_card, graphics_card)
self
end
def with_operating_system(operating_system)
@computer = @computer.dup
@computer.instance_variable_set(:@operating_system, operating_system)
self
end
def build
@computer
end
end
# Example Usage
builder = ComputerBuilder.new
gaming_pc = builder.with_cpu("Intel i9").with_ram("32GB").with_graphics_card("Nvidia RTX 4090").with_storage("2TB SSD").with_operating_system("Windows 11").build
office_pc = builder.with_cpu("Intel i5").with_ram("16GB").with_storage("512GB SSD").with_operating_system("Windows 10").build
puts gaming_pc
puts office_pc
The Builder pattern is a creational design pattern that lets you construct complex objects step by step. The pattern allows separation of construction from representation, meaning the same construction process can create different representations. This is achieved by creating a separate builder class which encapsulates the construction process, providing methods for each step. A director class (often optional) can orchestrate the builder to create a specific object configuration.
This Swift implementation uses a Pizza struct representing the complex object and a PizzaBuilder class to handle its construction. The buildCrust(), buildSauce(), buildTopping() methods add components incrementally. A Waiter class acts as the director, defining common pizza recipes. The code leverages Swift’s structs for data representation and classes for behavior, aligning with its preferred style. The use of associated values in the PizzaBuilder allows for flexible configuration.
// Product
struct Pizza {
let crust: String
let sauce: String
let topping: [String]
}
// Builder Interface
protocol PizzaBuilder {
mutating func buildCrust() -> Self
mutating func buildSauce() -> Self
mutating func buildTopping(topping: String) -> Self
func getPizza() -> Pizza
}
// Concrete Builder
class MargheritaPizzaBuilder: PizzaBuilder {
private var crust: String = ""
private var sauce: String = ""
private var topping: [String] = []
func buildCrust() -> Self {
self.crust = "Thin Crust"
return self
}
func buildSauce() -> Self {
self.sauce = "Tomato Sauce"
return self
}
func buildTopping(topping: String) -> Self {
self.topping.append(topping)
return self
}
func getPizza() -> Pizza {
return Pizza(crust: crust, sauce: sauce, topping: topping)
}
}
// Director (Optional)
class Waiter {
private var builder: PizzaBuilder
init(builder: PizzaBuilder) {
self.builder = builder
}
func constructMargherita() -> Pizza {
return builder
.buildCrust()
.buildSauce()
.buildTopping(topping: "Mozzarella")
.getPizza()
}
func constructPepperoni() -> Pizza {
return builder
.buildCrust()
.buildSauce()
.buildTopping(topping: "Pepperoni")
.buildTopping(topping: "Mozzarella")
.getPizza()
}
}
// Usage
let margheritaBuilder = MargheritaPizzaBuilder()
let waiter = Waiter(builder: margheritaBuilder)
let margherita = waiter.constructMargherita()
print(margherita)
let pepperoni = waiter.constructPepperoni()
print(pepperoni)
The Builder pattern is a creational design pattern that lets you construct complex objects step-by-step. It allows for creating different representations of an object using the same construction process. This is particularly useful when an object has many optional parameters.
The Kotlin code implements the Builder pattern for a Computer object. A separate ComputerBuilder class handles the construction process, allowing setting components like CPU, RAM, and storage individually. The build() method then assembles the final Computer instance. Kotlin’s concise syntax and data classes make this pattern particularly elegant, avoiding excessive boilerplate often found in other languages. The use of named and default arguments further enhances readability and flexibility.
data class Computer(
val cpu: String,
val ram: Int,
val storage: String = "SSD",
val graphicsCard: String? = null,
val operatingSystem: String = "Linux"
)
class ComputerBuilder {
private var cpu: String = ""
private var ram: Int = 0
private var storage: String = "SSD"
private var graphicsCard: String? = null
private var operatingSystem: String = "Linux"
fun withCpu(cpu: String): ComputerBuilder {
this.cpu = cpu
return this
}
fun withRam(ram: Int): ComputerBuilder {
this.ram = ram
return this
}
fun withStorage(storage: String): ComputerBuilder {
this.storage = storage
return this
}
fun withGraphicsCard(graphicsCard: String?): ComputerBuilder {
this.graphicsCard = graphicsCard
return this
}
fun withOperatingSystem(operatingSystem: String): ComputerBuilder {
this.operatingSystem = operatingSystem
return this
}
fun build(): Computer {
return Computer(cpu, ram, storage, graphicsCard, operatingSystem)
}
}
fun main() {
val gamingComputer = ComputerBuilder()
.withCpu("Intel i9")
.withRam(32)
.withGraphicsCard("Nvidia RTX 4090")
.build()
val basicComputer = ComputerBuilder()
.withCpu("AMD Ryzen 5")
.withRam(16)
.build()
println(gamingComputer)
println(basicComputer)
}
The Builder pattern allows for the construction of complex objects step-by-step. It separates the object construction logic from its representation, enabling the creation of different variations of the object without altering the core construction process. This is achieved through a builder struct that holds the object’s attributes and provides methods to set them. A final build() method then assembles the object.
The Rust implementation uses a struct for the builder, holding the fields of the target object. Methods on the builder return self to enable chaining. The build() method consumes the builder and returns the constructed object. This approach leverages Rust’s ownership and move semantics for safety and efficiency, and the fluent interface created by chaining methods is a common Rust idiom for configuration.
// Target object
#[derive(Debug)]
struct Computer {
cpu: String,
ram: u32,
storage: String,
has_gpu: bool,
}
// Builder struct
struct ComputerBuilder {
cpu: String,
ram: u32,
storage: String,
has_gpu: bool,
}
impl ComputerBuilder {
fn new() -> Self {
ComputerBuilder {
cpu: String::from("Intel i5"),
ram: 8,
storage: String::from("HDD"),
has_gpu: false,
}
}
fn cpu(mut self, cpu: String) -> Self {
self.cpu = cpu;
self
}
fn ram(mut self, ram: u32) -> Self {
self.ram = ram;
self
}
fn storage(mut self, storage: String) -> Self {
self.storage = storage;
self
}
fn has_gpu(mut self, has_gpu: bool) -> Self {
self.has_gpu = has_gpu;
self
}
fn build(self) -> Computer {
Computer {
cpu: self.cpu,
ram: self.ram,
storage: self.storage,
has_gpu: self.has_gpu,
}
}
}
fn main() {
let gaming_pc = ComputerBuilder::new()
.cpu(String::from("Intel i9"))
.ram(32)
.storage(String::from("SSD"))
.has_gpu(true)
.build();
let office_pc = ComputerBuilder::new()
.ram(16)
.storage(String::from("SSD"))
.build();
println!("Gaming PC: {:?}", gaming_pc);
println!("Office PC: {:?}", office_pc);
}
The Builder pattern is a creational design pattern that lets you construct complex objects step by step. The pattern allows for the separation of construction from representation, making it easier to create different variations of an object without changing the core construction logic.
This Go implementation defines a Product struct representing the complex object and a Builder interface outlining the steps to build it. A concrete ConcreteBuilder struct implements the interface, accumulating parts of the product in its internal state. A Director struct orchestrates the building process using the builder. The GetProduct() method retrieves the final built product. This approach is idiomatic Go as it leverages interfaces for flexibility and composition over inheritance, aligning with Go’s design principles.
// Product represents the complex object being built.
type Product struct {
PartA string
PartB string
PartC string
}
// Builder defines the interface for building a Product.
type Builder interface {
SetPartA(partA string) Builder
SetPartB(partB string) Builder
SetPartC(partC string) Builder
GetProduct() *Product
}
// ConcreteBuilder implements the Builder interface.
type ConcreteBuilder struct {
product *Product
}
// NewConcreteBuilder creates a new ConcreteBuilder.
func NewConcreteBuilder() *ConcreteBuilder {
return &ConcreteBuilder{
product: &Product{},
}
}
func (b *ConcreteBuilder) SetPartA(partA string) Builder {
b.product.PartA = partA
return b
}
func (b *ConcreteBuilder) SetPartB(partB string) Builder {
b.product.PartB = partB
return b
}
func (b *ConcreteBuilder) SetPartC(partC string) Builder {
b.product.PartC = partC
return b
}
func (b *ConcreteBuilder) GetProduct() *Product {
return b.product
}
// Director orchestrates the building process.
type Director struct {
builder Builder
}
// NewDirector creates a new Director.
func NewDirector(builder Builder) *Director {
return &Director{builder: builder}
}
// Construct builds a Product using the provided Builder.
func (d *Director) Construct(partA, partB, partC string) *Product {
d.builder.SetPartA(partA).SetPartB(partB).SetPartC(partC)
return d.builder.GetProduct()
}
The Builder pattern is a creational design pattern that lets you construct complex objects step by step. The pattern allows for the separation of construction from representation, making it suitable when creating an object requires many optional parameters or complex initialization logic. This implementation uses a struct to represent the object being built and a builder struct with methods to set individual parts. A build() method in the builder then finalizes the object creation. This approach is idiomatic C as it leverages structs for data aggregation and function pointers (implicitly through method calls) to manage the construction process, avoiding the complexities of C++ classes while achieving similar design goals.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Product
typedef struct {
char* name;
int age;
char* occupation;
int years_experience;
} Person;
// Builder
typedef struct {
Person person;
} PersonBuilder;
// Builder methods
PersonBuilder* person_builder_create() {
PersonBuilder* builder = (PersonBuilder*)malloc(sizeof(PersonBuilder));
if (!builder) {
perror("Failed to allocate memory for PersonBuilder");
exit(EXIT_FAILURE);
}
return builder;
}
PersonBuilder* person_builder_set_name(PersonBuilder* builder, const char* name) {
builder->person.name = strdup(name);
return builder;
}
PersonBuilder* person_builder_set_age(PersonBuilder* builder, int age) {
builder->person.age = age;
return builder;
}
PersonBuilder* person_builder_set_occupation(PersonBuilder* builder, const char* occupation) {
builder->person.occupation = strdup(occupation);
return builder;
}
PersonBuilder* person_builder_set_years_experience(PersonBuilder* builder, int years_experience) {
builder->person.years_experience = years_experience;
return builder;
}
// Build method
Person* person_builder_build(PersonBuilder* builder) {
Person* person = (Person*)malloc(sizeof(Person));
if (!person) {
perror("Failed to allocate memory for Person");
exit(EXIT_FAILURE);
}
*person = builder->person; // Copy the built person
return person;
}
void person_print(const Person* person) {
printf("Name: %s\n", person->name);
printf("Age: %d\n", person->age);
printf("Occupation: %s\n", person->occupation);
printf("Years of Experience: %d\n", person->years_experience);
}
int main() {
PersonBuilder* builder = person_builder_create();
Person* person1 = person_builder_set_name(builder, "Alice")
->person_builder_set_age(builder, 30)
->person_builder_set_occupation(builder, "Engineer")
->person_builder_build();
Person* person2 = person_builder_set_name(builder, "Bob")
->person_builder_set_age(builder, 25)
->person_builder_build();
person_print(person1);
person_print(person2);
free(person1->name);
free(person1->occupation);
free(person1);
free(person2->name);
free(person2);
free(builder);
return 0;
}
The Builder pattern is a creational design pattern that lets you construct complex objects step-by-step. It allows separation of construction from representation, making the process more flexible and readable, especially when dealing with objects that have many optional parameters.
The C++ implementation uses a separate Builder class with methods for each configurable part of the product (Computer). A Director class orchestrates the building process, using the builder to create the product. This approach avoids telescoping constructors and provides a clear, step-by-step construction process. The use of a dedicated builder class and a director aligns with C++’s emphasis on encapsulation and separation of concerns, promoting maintainability and extensibility.
#include <iostream>
#include <string>
#include <vector>
// Product
class Computer {
public:
Computer(std::string cpu, std::string ram, std::string storage, bool hasGpu, std::string os)
: cpu_(cpu), ram_(ram), storage_(storage), hasGpu_(hasGpu), os_(os) {}
void display() const {
std::cout << "CPU: " << cpu_ << std::endl;
std::cout << "RAM: " << ram_ << std::endl;
std::cout << "Storage: " << storage_ << std::endl;
std::cout << "GPU: " << (hasGpu_ ? "Yes" : "No") << std::endl;
std::cout << "OS: " << os_ << std::endl;
}
private:
std::string cpu_;
std::string ram_;
std::string storage_;
bool hasGpu_;
std::string os_;
};
// Builder Interface
class ComputerBuilder {
public:
virtual ~ComputerBuilder() = default;
virtual ComputerBuilder& setCPU(std::string cpu) = 0;
virtual ComputerBuilder& setRAM(std::string ram) = 0;
virtual ComputerBuilder& setStorage(std::string storage) = 0;
virtual ComputerBuilder& setGPU(bool hasGpu) = 0;
virtual ComputerBuilder& setOS(std::string os) = 0;
virtual Computer build() = 0;
};
// Concrete Builder
class GamingComputerBuilder : public ComputerBuilder {
public:
GamingComputerBuilder() : cpu_("Intel i9"), ram_("32GB"), storage_("1TB SSD"), hasGpu_(true), os_("Windows 11") {}
ComputerBuilder& setCPU(std::string cpu) override {
cpu_ = cpu;
return *this;
}
ComputerBuilder& setRAM(std::string ram) override {
ram_ = ram;
return *this;
}
ComputerBuilder& setStorage(std::string storage) override {
storage_ = storage;
return *this;
}
ComputerBuilder& setGPU(bool hasGpu) override {
hasGpu_ = hasGpu;
return *this;
}
ComputerBuilder& setOS(std::string os) override {
os_ = os;
return *this;
}
Computer build() override {
return Computer(cpu_, ram_, storage_, hasGpu_, os_);
}
private:
std::string cpu_;
std::string ram_;
std::string storage_;
bool hasGpu_;
std::string os_;
};
// Director
class ComputerDirector {
public:
ComputerDirector(ComputerBuilder* builder) : builder_(builder) {}
void constructGamingComputer() {
builder_->setCPU("AMD Ryzen 9")
->setRAM("64GB")
->setStorage("2TB NVMe SSD")
->setGPU(true)
->setOS("Linux");
}
Computer getComputer() {
return builder_->build();
}
private:
ComputerBuilder* builder_;
};
int main() {
GamingComputerBuilder builder;
ComputerDirector director(&builder);
director.constructGamingComputer();
Computer computer = director.getComputer();
computer.display();
return 0;
}
The Builder pattern is a creational design pattern that lets you construct complex objects step by step. It allows for the separation of construction logic from the object’s representation, enabling different variations of the object to be created using the same construction process.
The C# code demonstrates the Builder pattern for constructing a Computer object. The ComputerBuilder class provides a fluent interface (WithProcessor, WithRam, WithStorage) to set the computer’s components. A Director class orchestrates the building process using the builder. This approach keeps the Computer class simple and focuses the construction complexity within the builder, promoting code readability and maintainability. The use of a fluent interface is a common and idiomatic practice in C# for builder patterns.
// Computer class - the complex object to build
public class Computer
{
public string Processor { get; set; }
public int Ram { get; set; }
public string Storage { get; set; }
public bool HasGpu { get; set; }
private Computer(string processor, int ram, string storage, bool hasGpu)
{
Processor = processor;
Ram = ram;
Storage = storage;
HasGpu = hasGpu;
}
public override string ToString()
{
return $"Processor: {Processor}, Ram: {Ram}GB, Storage: {Storage}, GPU: {HasGpu}";
}
// Inner Builder class
public class Builder
{
private string processor;
private int ram;
private string storage;
private bool hasGpu;
public Builder WithProcessor(string processor)
{
this.processor = processor;
return this;
}
public Builder WithRam(int ram)
{
this.ram = ram;
return this;
}
public Builder WithStorage(string storage)
{
this.storage = storage;
return this;
}
public Builder WithGpu(bool hasGpu)
{
this.hasGpu = hasGpu;
return this;
}
public Computer Build()
{
return new Computer(processor, ram, storage, hasGpu);
}
}
}
// Director class - orchestrates the building process
public class Director
{
public Computer ConstructGamingComputer(Computer.Builder builder)
{
return builder.WithProcessor("Intel i9")
.WithRam(32)
.WithStorage("2TB SSD")
.WithGpu(true)
.Build();
}
public Computer ConstructOfficeComputer(Computer.Builder builder)
{
return builder.WithProcessor("Intel i5")
.WithRam(16)
.WithStorage("512GB SSD")
.WithGpu(false)
.Build();
}
}
// Example Usage
public class Example
{
public static void Main(string[] args)
{
Director director = new Director();
Computer gamingComputer = director.ConstructGamingComputer(new Computer.Builder());
Console.WriteLine(gamingComputer);
Computer officeComputer = director.ConstructOfficeComputer(new Computer.Builder());
Console.WriteLine(officeComputer);
}
}
The Builder pattern is a creational design pattern that lets you construct complex objects step by step. It allows for the separation of construction logic from the object’s representation, enabling different variations of the object to be created using the same construction process.
This TypeScript implementation defines an IPizza interface and a Pizza class representing the final product. The PizzaBuilder class provides a fluent interface with methods for setting each part of the pizza (dough, sauce, toppings). A Director class orchestrates the building process, using the builder to create specific pizza types. This approach is idiomatic TypeScript due to its use of interfaces for type safety, classes for structure, and method chaining for a clean, readable API.
// Define the product
interface IPizza {
getDough(): string;
getSauce(): string;
getToppings(): string[];
}
class Pizza implements IPizza {
private dough: string;
private sauce: string;
private toppings: string[];
constructor(dough: string, sauce: string, toppings: string[]) {
this.dough = dough;
this.sauce = sauce;
this.toppings = toppings;
}
getDough(): string {
return this.dough;
}
getSauce(): string {
return this.sauce;
}
getToppings(): string[] {
return this.toppings;
}
}
// Define the Builder interface
interface PizzaBuilder {
reset(): PizzaBuilder;
setDough(dough: string): PizzaBuilder;
setSauce(sauce: string): PizzaBuilder;
addTopping(topping: string): PizzaBuilder;
getPizza(): Pizza;
}
// Concrete Builder
class ConcretePizzaBuilder implements PizzaBuilder {
private dough: string;
private sauce: string;
private toppings: string[] = [];
reset(): PizzaBuilder {
this.dough = "";
this.sauce = "";
this.toppings = [];
return this;
}
setDough(dough: string): PizzaBuilder {
this.dough = dough;
return this;
}
setSauce(sauce: string): PizzaBuilder {
this.sauce = sauce;
return this;
}
addTopping(topping: string): PizzaBuilder {
this.toppings.push(topping);
return this;
}
getPizza(): Pizza {
const pizza = new Pizza(this.dough, this.sauce, this.toppings);
return pizza;
}
}
// Director
class Director {
constructor(private builder: PizzaBuilder) {}
public constructMargherita(): Pizza {
return this.builder
.reset()
.setDough("thin crust")
.setSauce("tomato sauce")
.addTopping("mozzarella")
.getPizza();
}
public constructHawaiian(): Pizza {
return this.builder
.reset()
.setDough("thick crust")
.setSauce("tomato sauce")
.addTopping("ham")
.addTopping("pineapple")
.addTopping("mozzarella")
.getPizza();
}
}
// Usage
const builder = new ConcretePizzaBuilder();
const director = new Director(builder);
const margherita = director.constructMargherita();
console.log("Margherita Pizza:");
console.log(` Dough: ${margherita.getDough()}`);
console.log(` Sauce: ${margherita.getSauce()}`);
console.log(` Toppings: ${margherita.getToppings().join(", ")}`);
const hawaiian = director.constructHawaiian();
console.log("\nHawaiian Pizza:");
console.log(` Dough: ${hawaiian.getDough()}`);
console.log(` Sauce: ${hawaiian.getSauce()}`);
console.log(` Toppings: ${hawaiian.getToppings().join(", ")}`);
The Builder pattern is a creational design pattern that lets you construct complex objects step by step. It allows for the separation of construction logic from the object’s representation, enabling different variations of the object to be created using the same construction process.
This JavaScript implementation uses a PizzaBuilder class to construct Pizza objects. The builder has methods for each component (dough, sauce, toppings) and a getResult() method to return the final pizza. A Director class orchestrates the building process, defining common pizza “recipes” (e.g., Margherita, Spicy). This approach is idiomatic JavaScript as it leverages object literals and classes for structure, and functions (methods) to encapsulate the building steps, promoting code reusability and readability.
// Product
class Pizza {
constructor(dough, sauce, toppings) {
this.dough = dough;
this.sauce = sauce;
this.toppings = toppings;
}
toString() {
return `Dough: ${this.dough}, Sauce: ${this.sauce}, Toppings: ${this.toppings.join(', ')}`;
}
}
// Builder
class PizzaBuilder {
constructor() {
this.dough = null;
this.sauce = null;
this.toppings = [];
}
reset() {
this.dough = null;
this.sauce = null;
this.toppings = [];
}
setDough(dough) {
this.dough = dough;
return this;
}
setSauce(sauce) {
this.sauce = sauce;
return this;
}
addTopping(topping) {
this.toppings.push(topping);
return this;
}
getResult() {
return new Pizza(this.dough, this.sauce, this.toppings);
}
}
// Director
class Director {
constructor(builder) {
this.builder = builder;
}
setBuilder(builder) {
this.builder = builder;
}
constructMargherita() {
this.builder.setDough('thin crust').setSauce('tomato').addTopping('mozzarella').addTopping('basil');
}
constructSpicy() {
this.builder.setDough('thick crust').setSauce('spicy marinara').addTopping('pepperoni').addTopping('jalapenos').addTopping('extra cheese');
}
}
// Usage
const builder = new PizzaBuilder();
const director = new Director(builder);
director.constructMargherita();
const margherita = builder.getResult();
console.log(margherita.toString());
builder.reset();
director.constructSpicy();
const spicy = builder.getResult();
console.log(spicy.toString());
The Builder pattern is a creational design pattern that lets you construct complex objects step by step. It allows for the separation of construction from representation, making the process more flexible and readable, especially when dealing with objects that have many optional attributes.
The Python code below demonstrates the Builder pattern for constructing Computer objects. A ComputerBuilder class provides methods to set individual components (CPU, RAM, Storage, etc.). A Director class orchestrates the building process using the builder. This approach keeps the Computer class simple and allows for different configurations without modifying the core class. Using methods for each component is a natural fit for Python’s object-oriented style, and the Director provides a clean interface for creating common configurations.
class Computer:
def __init__(self):
self.cpu = None
self.ram = None
self.storage = None
self.graphics_card = None
self.operating_system = None
def __str__(self):
return (
f"Computer:\n"
f" CPU: {self.cpu}\n"
f" RAM: {self.ram}\n"
f" Storage: {self.storage}\n"
f" Graphics Card: {self.graphics_card}\n"
f" Operating System: {self.operating_system}"
)
class ComputerBuilder:
def __init__(self):
self.computer = Computer()
def with_cpu(self, cpu):
self.computer.cpu = cpu
return self
def with_ram(self, ram):
self.computer.ram = ram
return self
def with_storage(self, storage):
self.computer.storage = storage
return self
def with_graphics_card(self, graphics_card):
self.computer.graphics_card = graphics_card
return self
def with_operating_system(self, operating_system):
self.computer.operating_system = operating_system
return self
def build(self):
return self.computer
class Director:
def __init__(self, builder):
self.builder = builder
def construct_gaming_computer(self):
return (
self.builder
.with_cpu("Intel i9")
.with_ram("32GB DDR5")
.with_storage("2TB NVMe SSD")
.with_graphics_card("NVIDIA RTX 4090")
.with_operating_system("Windows 11")
.build()
)
def construct_office_computer(self):
return (
self.builder
.with_cpu("Intel i5")
.with_ram("16GB DDR4")
.with_storage("512GB SSD")
.with_operating_system("Windows 10")
.build()
)
if __name__ == "__main__":
builder = ComputerBuilder()
director = Director(builder)
gaming_computer = director.construct_gaming_computer()
print(gaming_computer)
office_computer = director.construct_office_computer()
print("\n" + str(office_computer))
The Builder pattern is a creational design pattern that lets you construct complex objects step-by-step. It allows for separating the construction of an object from its representation, especially useful when an object has many optional parameters. This implementation uses a static inner Builder class to handle the object construction. The Builder class accumulates the necessary parameters through setter methods and then creates the final object with a build() method. This approach is idiomatic Java as it promotes immutability of the constructed object and provides a fluent interface for building it, enhancing readability and maintainability.
// Computer.java
public class Computer {
private final String cpu;
private final int ram;
private final String storage;
private final boolean hasGpu;
private final String operatingSystem;
private Computer(ComputerBuilder builder) {
this.cpu = builder.cpu;
this.ram = builder.ram;
this.storage = builder.storage;
this.hasGpu = builder.hasGpu;
this.operatingSystem = builder.operatingSystem;
}
public String getCpu() {
return cpu;
}
public int getRam() {
return ram;
}
public String getStorage() {
return storage;
}
public boolean hasGpu() {
return hasGpu;
}
public String getOperatingSystem() {
return operatingSystem;
}
@Override
public String toString() {
return "Computer{" +
"cpu='" + cpu + '\'' +
", ram=" + ram +
", storage='" + storage + '\'' +
", hasGpu=" + hasGpu +
", operatingSystem='" + operatingSystem + '\'' +
'}';
}
public static class ComputerBuilder {
private String cpu;
private int ram;
private String storage;
private boolean hasGpu;
private String operatingSystem;
public ComputerBuilder(String cpu, int ram) {
this.cpu = cpu;
this.ram = ram;
}
public ComputerBuilder storage(String storage) {
this.storage = storage;
return this;
}
public ComputerBuilder hasGpu(boolean hasGpu) {
this.hasGpu = hasGpu;
return this;
}
public ComputerBuilder operatingSystem(String operatingSystem) {
this.operatingSystem = operatingSystem;
return this;
}
public Computer build() {
return new Computer(this);
}
}
public static void main(String[] args) {
Computer computer = new ComputerBuilder("Intel i7", 16)
.storage("512GB SSD")
.hasGpu(true)
.operatingSystem("Windows 11")
.build();
System.out.println(computer);
}
}