Design Pattern Series - Builder (Golang)

Go Design Pattern Series 28 Th08 2025

Bài viết trước trong series Singleton Pattern

Khi code Go, chắc hẳn bạn từng gặp các case cần tạo ra struct với rất nhiều tham số, bao gồm cả optional và required. Nếu thêm tất cả vào một constructor thì code sẽ trở nên khó đọc và dễ lỗi.

--> Sau khi tìm hiểu thì mình tìm thấy Builder Pattern, và đây là cách pattern này giúp mình tối ưu lại vấn đề này.

Builder Pattern là gì?

Đây là một pattern thuộc nhóm Creational Design Patterns, ý tưởng chính của nó bao gồm (mình giữ tiếng anh để tránh việc khó hiểu khi translate sang tiếng việt):

  • Separate the construction of a complex object from its representation -> thay vì tạo 1 struct với constructor dài, ta chia nhỏ quá trình khởi tạo ra thành từng bước, rồi cuối cùng mới build ra struct hoàn chỉnh (representation)
  • Step-by-step construction -> set từng thuộc tính của 1 struct một cách tuần tự
  • Avoid telescoping constructors -> Khi struct có quá nhiều tham số optional, việc tạo truyền thống sẽ hơi khó đọc (NewCar("vinfast", "VF9", "green", "$100000", .....)). Với builder, bạn trách được việc phải pass quá nhiều tham số vào một lúc.

Ví dụ không sử dụng 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)

--> Ở đây, việc tạo struct (Car) và representation (car) bị gộp chung một chỗ.

  • Bạn phải biết hết tất cả tham số.
  • Constructor dễ trở nên rối rắm nếu có nhiều tham số tùy chọn

Khi sử dụng builder

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

--> Ở đây:

  • Quá trình tạo (construction process): SetBrand, SetModel, SetColor
  • Representation: car (một instance của Car).

Tức là, bạn không trực tiếp tạo Car ngay, mà thông qua một “người xây dựng” (CarBuilder) để chuẩn bị từng phần, rồi mới gọi Build() để xuất ra đối tượng hoàn chỉnh

Như vậy, Builder Pattern đơn giản là:

  • Không ép bạn phải tạo struct ngay lập tức.
  • Cho phép bạn lắp ráp từng phần, dễ đọc, dễ hiểu.
  • Tránh việc constructor quá tải tham số.

--> Khi nào thì dùng Builder Pattern?

  • Khi struct có nhiều tham số
  • Cần code dễ đọc và bảo trì hơn
  • Cần khởi tạo với các cách khác nhau

Triển khai Builder Pattern

Giả sử, ta cần tạo ra 1 struct cho Car với nhiều thuộc tính khác nhau như: hãng, dòng xe, màu sắc, giá tiền, mã lực, nguyên liệu, số ghế, cốp...

Định nghĩa struct CarCarBuilder

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,
	}
}

Và sử dụng Builder

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

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

Kết quả trả ra

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

Tuy nhiên thì Builder Pattern cũng có 1 số hạn chế như bạn thấy:

  • Code dài hơn để định nghĩa builder
  • Struct ít tham số có thể gây overkill (phức tạp hoá vấn đề)

Kết luận

Builder Pattern trong Golang là một lựa chọn tốt khi bạn cần tạo ra những đối tượng phức tạp với nhiều tham số tùy chọn. Nó giúp code trở nên rõ ràng, dễ đọc và dễ mở rộng hơn

Thanks for reading!

Chuyên mục