Design Pattern Series - Builder (Golang)

Go Design Pattern Series Aug 28, 2025

Previous post in series Singleton Pattern

When coding in Go, you must have encountered cases where you need to create a struct with a lot of parameters, including optional and required. If you add all of them to a constructor, the code will become difficult to read and prone to errors.

--> After researching, I found the Builder Pattern, and this is how this pattern helps me optimize this problem.

What is the Builder Pattern?

This is a pattern belonging to the Creational Design Patterns group, its main idea includes (I keep it in English to avoid confusion when translating to Vietnamese):

  • Separate the construction of a complex object from its representation -> instead of creating a struct with a long constructor, we split the initialization process into steps, and then finally build the complete struct (representation)
  • Step-by-step construction -> set each property of a struct sequentially
  • Avoid telescoping constructors -> When a struct has too many optional parameters, traditional creation will be a bit difficult to read (NewCar("vinfast", "VF9", "green", "$100000", .....)). With the builder, you avoid having to pass too many parameters at once.

Example without Builder:

car := Car{
    Brand:   "Toyota",
    Model:   "Camry",
    Color:   "Black",
    Engine:  "V6",
    Seats:   5,
    Sunroof: true,
}

// or use a constructor
car := NewCar("Toyota", "Camry", "Black", "V6", 5, true)

--> Here, the creation of the struct (Car) and the representation (car) are combined in one place.

  • You must know all the parameters.
  • Constructors can easily become confusing if there are many optional parameters.

With builder

car := NewCarBuilder().
    SetBrand("Toyota").
    SetModel("Camry").
    SetColor("Black").
    SetEngine("V6").
    SetSeats(5).
    SetSunroof(true).
    Build()

--> Here:

  • Construction process: SetBrand, SetModel, SetColor
  • Representation: car (an instance of Car).

That is, you do not directly create a Car, but use a “builder” (CarBuilder) to prepare each part, then call Build() to output the complete object.

So, the Builder Pattern is simply:

  • Don't force you to create a struct immediately.
  • Allows you to assemble it piece by piece, easy to read, easy to understand.
  • Avoid constructor overloading.

--> When to use the the Builder Pattern?

  • When a struct has many parameters
  • Need more readable and maintainable code
  • Need to initialize in different ways

Implement the Builder Pattern in Go

Suppose we need to create a struct for a Car with many different attributes, such as brand, model, color, price, horsepower, material, number of seats, trunk...

Define struct Car and CarBuilder

package main

import "fmt"

// Product: Car
type Car struct {
	Brand   string
	Model   string
	Color   string
	Engine  string
	Seats   int
	Sunroof bool
}

// Builder
type CarBuilder struct {
	brand   string
	model   string
	color   string
	engine  string
	seats   int
	sunroof bool
}

func NewCarBuilder() *CarBuilder {
	return &CarBuilder{}
}

func (b *CarBuilder) SetBrand(brand string) *CarBuilder {
	b.brand = brand
	return b
}

func (b *CarBuilder) SetModel(model string) *CarBuilder {
	b.model = model
	return b
}

func (b *CarBuilder) SetColor(color string) *CarBuilder {
	b.color = color
	return b
}

func (b *CarBuilder) SetEngine(engine string) *CarBuilder {
	b.engine = engine
	return b
}

func (b *CarBuilder) SetSeats(seats int) *CarBuilder {
	b.seats = seats
	return b
}

func (b *CarBuilder) SetSunroof(sunroof bool) *CarBuilder {
	b.sunroof = sunroof
	return b
}

func (b *CarBuilder) Build() Car {
	return Car{
		Brand:   b.brand,
		Model:   b.model,
		Color:   b.color,
		Engine:  b.engine,
		Seats:   b.seats,
		Sunroof: b.sunroof,
	}
}

And uses it

func main() {
	car := NewCarBuilder().
		SetBrand("Toyota").
		SetModel("Camry").
		SetColor("Black").
		SetEngine("V6").
		SetSeats(5).
		SetSunroof(true).
		Build()

	fmt.Printf("%+v\n", car)
}

Output

{Brand:Toyota Model:Camry Color:Black Engine:V6 Seats:5 Sunroof:true}

However, the Builder Pattern also has some limitations, as you can see:

  • Longer code to define the builder
  • Structs with few parameters can cause overkill

Conclude

Builder Pattern in Golang is a good choice when you need to create complex objects with many optional parameters. It makes the code clearer, easier to read, and easier to extend.

Thanks for reading!

Tags