Nội dung
Builder là một mẫu thiết kế khởi tạo, cho phép xây dựng các đối tượng phức tạp theo từng bước.
Không giống như các mẫu khởi tạo khác, Builder không yêu cầu các sản phẩm phải có giao diện chung. Điều đó làm cho nó có thể tạo các sản phẩm khác nhau bằng cách sử dụng cùng một quy trình xây dựng.
Sử dụng mẫu trong C++
Sử dụng: Mẫu Builder là một mẫu nổi tiếng trong C++. Nó đặc biệt hữu ích khi bạn cần tạo một đối tượng với nhiều tùy chọn cấu hình khả thi.
Nhận biết: Mẫu Builder có thể được nhận dạng trong một lớp, lớp này có một phương thức tạo duy nhất và một số phương thức để cấu hình đối tượng kết quả. Các phương thức Builder thường hỗ trợ chuỗi (ví dụ: someBuilder-> setValueA(1) -> setValueB(2) -> create()).
Chương trình minh họa
Ví dụ này minh họa cấu trúc của mẫu thiết kế Builder. Nó tập trung vào việc trả lời những câu hỏi sau:
- Nó bao gồm những lớp nào?
- Các lớp này có vai trò gì?
- Các thành phần của mẫu có liên quan với nhau theo cách nào?
main.cpp
/**
* Chỉ nên sử dụng mẫu Builder khi sản phẩm của bạn khá phức tạp
* và yêu cầu cấu hình có thể mở rộng.
*
* Không giống như trong các mẫu khởi tạo khác, các builder khác nhau có thể tạo ra
* các sản phẩm không liên quan. Nói cách khác, kết quả của các builder khác nhau
* có thể không phải lúc nào cũng tuân theo cùng một giao diện.
*/
class Product1{
public:
std::vector<std::string> parts_;
void ListParts()const{
std::cout << "Product parts: ";
for (size_t i=0;i<parts_.size();i++){
if(parts_[i]== parts_.back()){
std::cout << parts_[i];
}else{
std::cout << parts_[i] << ", ";
}
}
std::cout << "\n\n";
}
};
/**
* Giao diện Builder chỉ định các phương thức để tạo các phần khác nhau
* của đối tượng sản phẩm.
*/
class Builder{
public:
virtual ~Builder(){}
virtual void ProducePartA() const =0;
virtual void ProducePartB() const =0;
virtual void ProducePartC() const =0;
};
/**
* Các lớp Concrete Builder tuân theo giao diện Builder và cung cấp các triển khai
* cụ thể của các bước xây dựng. Chương trình của bạn có thể có một số biến
* thể của Builder, được triển khai khác nhau.
*/
class ConcreteBuilder1 : public Builder{
private:
Product1* product;
/**
* Một thể hiện builder mới phải chứa một đối tượng sản phẩm trống, được
* sử dụng trong quá trình lắp ráp tiếp theo.
*/
public:
ConcreteBuilder1(){
this->Reset();
}
~ConcreteBuilder1(){
delete product;
}
void Reset(){
this->product= new Product1();
}
/**
* Tất cả các bước sản xuất đều hoạt động với cùng một thể hiện.
*/
void ProducePartA()const override{
this->product->parts_.push_back("PartA1");
}
void ProducePartB()const override{
this->product->parts_.push_back("PartB1");
}
void ProducePartC()const override{
this->product->parts_.push_back("PartC1");
}
/**
* Các Concrete Builder phải cung cấp các phương pháp của riêng họ để lấy kết
* quả. Đó là bởi vì nhiều kiểu builder khác nhau có thể tạo ra các sản phẩm hoàn
* toàn khác nhau không tuân theo cùng một giao diện. Do đó, các phương thức
* như vậy không thể được khai báo trong giao diện Builder cơ sở.
*
* Thông thường, sau khi trả lại kết quả cuối cùng cho client, một builder dự kiến
* sẽ sẵn sàng để bắt đầu tạo một sản phẩm khác. Đó là lý do tại sao thường gọi
* phương thức reset ở cuối thân phương thức getProduct. Tuy nhiên, việc này
* là không bắt buộc và bạn có thể để các builder đợi lệnh gọi reset tường minh
* từ client trước khi loại bỏ kết quả trước đó.
*/
/**
* Hãy cẩn thận ở chổ này với quyền sở hữu bộ nhớ. Sau khi bạn gọi GetProduct,
* người dùng hàm này có trách nhiệm giải phóng bộ nhớ này. Có thể có lựa chọn
* tốt hơn là sử dụng smart pointer để tránh rò rỉ bộ nhớ.
*/
Product1* GetProduct() {
Product1* result= this->product;
this->Reset();
return result;
}
};
/**
* Director chỉ chịu trách nhiệm thực hiện các bước xây dựng theo một trình tự cụ
* thể. Nó rất hữu ích khi sản xuất sản phẩm theo trật tự hoặc cấu hình cụ thể.
* Nói một cách chính xác, lớp Director là tùy chọn, vì client có thể kiểm soát
* trực tiếp các builder.
*/
class Director{
/**
* @var Builder
*/
private:
Builder* builder;
/**
* Director làm việc với bất kỳ thể hiện builder nào mà client truyền đến nó.
* Bằng cách này, client có thể thay đổi kiểu cuối cùng của sản phẩm
* mới được lắp ráp.
*/
public:
void set_builder(Builder* builder){
this->builder=builder;
}
/**
* Director có thể xây dựng một số biến thể sản phẩm bằng các
* bước xây dựng giống nhau.
*/
void BuildMinimalViableProduct(){
this->builder->ProducePartA();
}
void BuildFullFeaturedProduct(){
this->builder->ProducePartA();
this->builder->ProducePartB();
this->builder->ProducePartC();
}
};
/**
* Client tạo một đối tượng builder, chuyển nó cho director và sau đó bắt đầu
* quá trình xây dựng. Kết quả cuối cùng được truy xuất từ đối tượng builder.
*/
/**
* Tôi đã sử dụng con trỏ thô vì sự đơn giản, tuy nhiên bạn có thể sử dụng
* smart pointer ở đây
*/
void ClientCode(Director& director)
{
ConcreteBuilder1* builder = new ConcreteBuilder1();
director.set_builder(builder);
std::cout << "Standard basic product:\n";
director.BuildMinimalViableProduct();
Product1* p= builder->GetProduct();
p->ListParts();
delete p;
std::cout << "Standard full featured product:\n";
director.BuildFullFeaturedProduct();
p= builder->GetProduct();
p->ListParts();
delete p;
// Hãy nhớ rằng, mẫu Builder có thể được sử dụng mà không có lớp Director.
std::cout << "Custom product:\n";
builder->ProducePartA();
builder->ProducePartC();
p=builder->GetProduct();
p->ListParts();
delete p;
delete builder;
}
int main(){
Director* director= new Director();
ClientCode(*director);
delete director;
return 0;
}
Kết quả:
Standard basic product:
Product parts: PartA1
Standard full featured product:
Product parts: PartA1, PartB1, PartC1
Custom product:
Product parts: PartA1, PartC1
Để lại một bình luận