SaigonTech Vault
Loại: knowledge-doc 2026-09-29 active #wiki-import

Builder thuộc nhóm creational (nhóm khởi tạo đối tượng). Nó cho phép bạn tạo ra các đối tượng phức tạp theo từng bước nhỏ.

Builder design pattern

Vấn đề

Hãy tưởng tượng một đối tượng phức tạp đòi hỏi nhiều công sức, khởi tạo từng bước của nhiều trường và các đối tượng lồng nhau. Mã khởi tạo như vậy thường được chôn bên trong một hàm tạo khổng lồ với rất nhiều tham số. Hoặc thậm chí tệ hơn: nằm rải rác trên toàn bộ mã khách hàng.

link

Ví dụ: hãy nghĩ về cách tạo đối tượng House. Để xây dựng một ngôi nhà đơn giản, bạn cần xây dựng bốn bức tường và một tầng, lắp cửa ra vào, lắp một cặp cửa sổ và xây dựng một mái nhà. Nhưng nếu bạn muốn một ngôi nhà lớn hơn, sáng sủa hơn, có sân sau và các tiện ích khác (như hệ thống sưởi, hệ thống ống nước và hệ thống dây điện)?

Giải pháp đơn giản nhất là mở rộng lớp House cơ sở và tạo một tập hợp các lớp con để bao gồm tất cả các tổ hợp của các tham số. Nhưng cuối cùng bạn sẽ có một số lượng đáng kể các lớp con. Bất kỳ thông số mới nào, chẳng hạn như kiểu hiên nhà, sẽ yêu cầu phát triển hệ thống phân cấp này nhiều hơn nữa.

Có một cách tiếp cận khác không liên quan đến việc lai tạo các lớp con. Bạn có thể tạo một phương thức khởi tạo khổng lồ ngay trong lớp House cơ sở với tất cả các tham số có thể có để điều khiển đối tượng House. Mặc dù cách tiếp cận này thực sự loại bỏ sự cần thiết của các lớp con, nhưng nó lại tạo ra một vấn đề khác.

The telescopic constructor

Trong hầu hết các trường hợp, hầu hết các tham số sẽ không được sử dụng, làm cho các cuộc gọi hàm tạo khá xấu xí. Ví dụ, chỉ một phần nhỏ các ngôi nhà có bể bơi, vì vậy các thông số liên quan đến bể bơi sẽ vô dụng chín lần trên mười.

Giải pháp

Mẫu Builder gợi ý rằng bạn trích xuất mã xây dựng đối tượng ra khỏi lớp của chính nó và di chuyển nó đến các đối tượng riêng biệt được gọi là builder.

Applying the Builder pattern

Mẫu sắp xếp việc xây dựng đối tượng thành một tập hợp các bước (buildWalls, buildDoor, v.v.). Để tạo một đối tượng, bạn thực hiện một loạt các bước này trên một đối tượng trình tạo. Phần quan trọng là bạn không cần phải gọi tất cả các bước. Bạn chỉ có thể gọi những bước cần thiết để tạo ra một cấu hình cụ thể của một đối tượng.

Một số bước xây dựng có thể yêu cầu thực hiện khác nhau khi bạn cần xây dựng các hình ảnh đại diện khác nhau của sản phẩm. Ví dụ, các bức tường của một cabin có thể được xây dựng bằng gỗ, nhưng các bức tường của lâu đài phải được xây dựng bằng đá.

Trong trường hợp này, bạn có thể tạo một số lớp trình xây dựng khác nhau triển khai cùng một tập hợp các bước xây dựng, nhưng theo một cách khác. Sau đó, bạn có thể sử dụng các trình xây dựng này trong quá trình xây dựng (tức là một tập hợp các lệnh gọi có thứ tự đến các bước xây dựng) để tạo ra các loại đối tượng khác nhau.

link

Ví dụ, hãy tưởng tượng một người thợ xây dựng mọi thứ từ gỗ và kính, người thứ hai xây dựng mọi thứ bằng đá và sắt và người thứ ba sử dụng vàng và kim cương. Bằng cách gọi cùng một nhóm các bước, bạn sẽ có được một ngôi nhà bình thường từ người xây dựng đầu tiên, một lâu đài nhỏ từ người thứ hai và một cung điện từ người thứ ba. Tuy nhiên, điều này sẽ chỉ hoạt động nếu mã máy khách gọi các bước xây dựng có thể tương tác với các nhà xây dựng bằng giao diện chung.

Director

Bạn có thể đi xa hơn và trích xuất một loạt các lệnh gọi đến các bước của trình tạo mà bạn sử dụng để xây dựng một sản phẩm thành một lớp riêng biệt có tên là Director. Lớp Director xác định thứ tự thực hiện các bước xây dựng, trong khi trình xây dựng cung cấp việc triển khai cho các bước đó.

link

Việc có một lớp Director trong chương trình của bạn là không hoàn toàn cần thiết. Bạn luôn có thể gọi các bước xây dựng theo thứ tự cụ thể trực tiếp từ mã khách hàng. Tuy nhiên, lớp Director có thể là một nơi tốt để đưa các quy trình xây dựng khác nhau để bạn có thể sử dụng lại chúng trong chương trình của mình.

Ngoài ra, Director hoàn toàn giấu kín các chi tiết cấu tạo sản phẩm với mã khách hàng. Khách hàng chỉ cần liên kết Builder với Director, khởi chạy công trình với Director và nhận kết quả từ Builder.

Cấu trúc

Structure of the Builder design pattern

Interface Builder

Khai báo các bước xây dựng sản phẩm chung cho tất cả các loại builder.

Concrete Builders

Cung cấp các cách triển khai khác nhau của các bước xây dựng. Concrete Builders có thể tạo ra các sản phẩm không tuân theo Interface chung.

Product

Là đối tượng kết quả. Các sản phẩm do các builder khác nhau tạo ra không nhất thiết phải thuộc cùng một hệ thống phân lớp hoặc interface.<br>

Lớp Director xác định thứ tự gọi các bước xây dựng, vì vậy bạn có thể tạo và sử dụng lại các cấu hình cụ thể của sản phẩm.

Client

Client phải liên kết một trong các đối tượng builder với director. <br>

Thông thường, nó chỉ được thực hiện một lần, thông qua các tham số của hàm tạo của director. Sau đó, director sử dụng đối tượng builder đó cho tất cả các công trình xây dựng tiếp theo. <br>

Tuy nhiên, có một cách tiếp cận thay thế khi khách hàng chuyển đối tượng builder sang phương pháp sản xuất của director. Trong trường hợp này, bạn có thể sử dụng một builder khác mỗi khi bạn sản xuất nội dung nào đó với director.

Ưu điểm

Bạn có thể xây dựng các đối tượng theo từng bước, trì hoãn các bước xây dựng hoặc chạy các bước một cách đệ quy.

Bạn có thể sử dụng lại cùng một mã xây dựng khi xây dựng các đại diện khác nhau của sản phẩm.

Single Responsibility Principle. Bạn có thể tách biệt mã xây dựng phức tạp khỏi logic hoạt động của sản phẩm.

Nhược điểm

Độ phức tạp tổng thể của mã tăng lên vì mẫu yêu cầu tạo nhiều lớp mới.

Nguồn: https://refactoring.guru/design-patterns/builder

Ví dụ bằng PHP: https://refactoring.guru/design-patterns/builder/php/example