Trong bài này mình sẽ giới thiệu Patterns cuối cùng trong nhóm Creational Patterns đó là Builder Patterns, một trong những design được sử dụng khá phổ biến.
Vấn đề
Vẫn là ví dụ về việc chọn xe như 2 bài trước Factory Method và Abstract Factory, nhưng hãy tưởng tượng giờ bạn không phải là Rich kid hay tỉ phú nữa, bạn là một dân chơi độ xe. Bạn đã quá nhàm chán với việc chọn các chiếc xe cố định mà bạn đang có rồi, giờ bạn muốn độ một chiếc xe mới với mỗi bộ phận mà bạn tùy thích, ví dụ như bạn muốn một chiếc xe với:
- Bánh xe của Toyota
- Động cơ của Ford
- Thiết kế của Ferrari
- Bộ điều khiển của BMW
- …
Chắc chắn Factory Method và Abstract Factory không thể giúp bạn giải quyết được vấn đề này, vì chúng phải được xây dựng trên một interface chung và không thể tùy biến một các chi tiết được
Giải pháp
Vậy trong trường hợp này chúng ta hoàn toàn có thể áp dụng Builder
Định nghĩa: “Separate the construction of a complex object from its representation so that the same construction process can create different representations.” (GOF)
Cụ thể nó sẽ giúp chúng ta tạo đối tượng phức tạo với nhiều cấu hình một cách tùy biến và từng phần của đối tượng là riêng biệt, hoàn toàn phù hợp với bài toán độ xe như ở trên khi chúng ta muốn tạo một chiếc xe mới với nhiều bộ phận được kết nối từ các hãng khác nhau và hoàn toàn độc lập
Giờ hãy thiết kế lại bài toán độ xe nhé:

Như các bạn thấy, chúng ta sẽ một inteface ICarBuilder có các method để build từng bộ phần của chiếc xe. Giờ mỗi khi chúng ta muốn build một chiếc xe mới thì chỉ cần implement theo inteface này tùy biến từng bộ phần mà ta muốn. Ví dụ như ta chỉ cần tạo thêm các Concrete Class như SportCar, LuxuryCar như trên.
Implement trong C++
Tạo object Car cho phép build từng bộ phận riêng biệt:
class Car{
public:
Car() = default;
std::string m_name;
std::unordered_map<std::string, std::string> m_parts;
void showInfo(){
std::cout << "The information about car: " << m_name << std::endl;
for(const auto& [part_name, info] : m_parts){
std::cout << "\t" << part_name << ": " << info << std::endl;
}
}
};
Tạo interface Builder và implement hai builders là SportCar và LuxuryCar:
class ICarBuilder{
public:
ICarBuilder() = default;
virtual ~ICarBuilder(){}
virtual ICarBuilder& setWheel() = 0;
virtual ICarBuilder& setEngine() = 0;
virtual ICarBuilder& setController() = 0;
virtual ICarBuilder& setDesign() = 0;
virtual Car& buildCar() = 0;
};
class LuxuryCar : public ICarBuilder{
public:
LuxuryCar(){}
~LuxuryCar(){}
LuxuryCar& setWheel() override{
m_car.m_parts.insert({"Wheel", "Roll Royce"});
return *this;
}
LuxuryCar& setEngine() override{
m_car.m_parts.insert({"Engine", "Ferrari"});
return *this;
}
LuxuryCar& setController() override{
m_car.m_parts.insert({"Controller", "Mercedes"});
return *this;
}
LuxuryCar& setDesign() override{
m_car.m_parts.insert({"Design", "BMW"});
return *this;
}
Car& buildCar(){
return m_car;
}
private:
Car m_car;
};
class SportCar : public ICarBuilder{
public:
SportCar(){}
~SportCar(){}
SportCar& setWheel() override{
m_car.m_parts.insert({"Wheel", "Ford"});
return *this;
}
SportCar& setEngine() override{
m_car.m_parts.insert({"Engine", "Bugatti"});
return *this;
}
SportCar& setController() override{
m_car.m_parts.insert({"Controller", "BMW"});
return *this;
}
SportCar& setDesign() override{
m_car.m_parts.insert({"Design", "Langborghini"});
return *this;
}
Car& buildCar(){
return m_car;
}
private:
Car m_car;
};
Bây giờ hãy xem cách sử dụng và kết quả nhé:
class Client{
public:
Client(){}
~Client(){}
void setBuilder(std::unique_ptr<ICarBuilder> builder){
m_builder = std::move(builder);
}
Car build(){
m_builder->setWheel().setDesign().setEngine().setController();
return m_builder->buildCar();
}
private:
std::unique_ptr<ICarBuilder> m_builder;
};
int main(){
auto client = std::make_unique<Client>();
// Build LuxuryCar
client->setBuilder(std::make_unique<LuxuryCar>());
client->build().showInfo();
// Build LuxuryCar
client->setBuilder(std::make_unique<SportCar>());
client->build().showInfo();
return 0;
}
Output

Ứng dụng
Builder Pattern được sử dụng khi:
- Cần tạo những đối tượng phức tạo một cách tùy biến và các bộ phận là độc lập với nhau
- Cần một giao diện định nghĩa xây dựng từng step của object
So sánh Builder và Factory Pattern
- Builder chia nhỏ việc xây dựng object theo từng step còn Factory thì tập trung vào việc phân nhóm các object được khởi tạo
- Builder sử dụng Composite còn Factory áp dụng Subclass
- Builder thì phức hơn nhưng linh hoạt hơn, ngược lại Factory đơn giản hơn.
Kết luận
Như vậy trong bài viết này mình đã giới thiệu về Builder Pattern, các sử dụng, và ứng dụng của nó.
Hi vọng bài viết hữu ích cho các bạn, nếu các bạn thấy bài viết hay thì hãy chia sẻ cho các anh em lập trình khác cùng biết nhé. Cám ơn các bạn đã ghé đọc ^^. Chúc các bạn làm việc hiệu quả ❤

Bình luận về bài viết này