一,建造者模式的定义
建造者模式,又被称为生成器模式,是一种创建型设计模式,它将复杂产品的构建过程分解为一系列简单的步骤,每个步骤由独立的建造者对象负责。
建造者模式常用于创建复杂的对象,它避免了直接传递大量参数来构造函数,使得构建过程变得可控,让代码变得灵活和可维护。
建造者模式允许开发者按照指定的步骤创建复杂对象,构建过程的细节被封装在具体建造者中,将创建对象的过程和表示对象的过程分离,且同一个构建过程可以使用不同的具体建造者以及不同的顺序来创建不同的表示。
建造者模式在现实生活中的抽象实例:
建筑分工:将一个复杂的建筑物分解成多个简单的部分,然后由不同的建筑工人来负责建造每个部分,最后将这些部分组装构建成完整的建筑物。
餐厅菜单:餐厅的菜单包含多个选项,如前菜、主菜、饮料和甜点,顾客可以根据他们的喜好和饥饿程度来定制自己的菜单。
汽车制造:每个车型具有不同的配置,如引擎类型、座位数量和外观,可以为每个车型创建对应的车辆建造者类。
电脑组装:每个电脑配置由不同的组件构成,如处理器、内存、硬盘和操作系统,客户可以根据自己的需求来定制电脑的配置。
假设要组装一辆汽车,可以设计一个汽车建造者类,它包含setEngine()、setWheels()、setInterior()等方法来添加汽车的各个组件,构建汽车的样例代码如下:
CarBuilder builder = new CarBuilder();
builder.setEngine("V6");
builder.setWheels(4);
builder.setLeatherInterior();
Car myCar = builder.build();
二,建造者模式的结构
建造者模式主要包含以下组件:
1.产品对象(Product):
需要被构建的复杂对象,它通常包含多个组件,每个组件都有自己的属性和行为。具体建造者负责创建和配置产品对象的各个组件。
2.建造者(Builder):
定义了构建产品对象的公共接口,它包含了创建产品对象各部分组件的抽象方法,以及返回产品对象的方法。
3.具体建造者(ConcreteBuilder):
包含了对Builder接口的具体实现,它通常有一个成员变量用于保存最终构建的产品对象。
4.指挥者(Director):
负责管理构建产品的全过程,它通常会接收一个具体建造者的实例作为参数,根据特定的构建顺序来构建产品。
组件之间的工作步骤如下:
1.Director收到构建请求后,根据具体建造者调用具体建造者的方法。
2.具体建造者根据构建请求创建新的产品对象,并使用一系列的方法设置产品各个组件的属性。
3.构建完成后,具体建造者将构建好的产品对象返回给Director。
4.Director可以选择从具体建造者获取产品对象,并执行进一步的操作,例如输出结果、序列化等。
对应UML类图:
三,建造者模式代码样例
Demo1:没有Director参与
#include <iostream>
#include <string>
// Product
class Product {
public:
void setPart1(int part1) {
part1_ = part1;
}
void setPart2(const std::string& part2) {
part2_ = part2;
}
void show() {
std::cout << "Part 1: "
<< part1_
<< "\nPart 2: "
<< part2_ << std::endl;
}
private:
int part1_;
std::string part2_;
};
// Builder
class Builder {
public:
virtual void buildPart1() = 0;
virtual void buildPart2() = 0;
virtual Product getResult() = 0;
};
// Concrete Builder
class ConcreteBuilder : public Builder {
public:
ConcreteBuilder() {
product_ = new Product();
}
void buildPart1() override {
product_->setPart1(30);
}
void buildPart2() override {
product_->setPart2("Part2 !");
}
Product getResult() override {
return *product_;
}
private:
Product* product_;
};
int main() {
ConcreteBuilder builder;
builder.buildPart1();
builder.buildPart2();
Product product = builder.getResult();
product.show();
return 0;
}
运行结果:
Part 1: 30
Part 2: Part2 !
Demo2:包含Director
#include <iostream>
#include <string>
// Product
class Product {
public:
void setPartA(const std::string& partA) {
partA_ = partA;
}
void setPartB(const std::string& partB) {
partB_ = partB;
}
void setPartC(const std::string& partC) {
partC_ = partC;
}
void show() {
std::cout << "Part A: " << partA_ << std::endl;
std::cout << "Part B: " << partB_ << std::endl;
std::cout << "Part C: " << partC_ << std::endl;
}
private:
std::string partA_;
std::string partB_;
std::string partC_;
};
// Builder
class Builder {
public:
virtual void buildPartA() = 0;
virtual void buildPartB() = 0;
virtual void buildPartC() = 0;
virtual Product* getProduct() = 0;
};
// Concrete Builder
class ConcreteBuilder : public Builder {
public:
ConcreteBuilder() {
product_ = new Product();
}
void buildPartA() {
product_->setPartA("Part A");
}
void buildPartB() {
product_->setPartB("Part B");
}
void buildPartC() {
product_->setPartC("Part C");
}
Product* getProduct() {
return product_;
}
private:
Product* product_;
};
// Director
class Director {
public:
void construct(Builder* builder) {
builder->buildPartA();
builder->buildPartB();
builder->buildPartC();
}
};
int main() {
ConcreteBuilder builder;
Director director;
director.construct(&builder);
Product* product = builder.getProduct();
product->show();
delete product;
return 0;
}
运行结果:
Part A: Part A
Part B: Part B
Part C: Part C
四,建造者模式的应用场景
图形用户界面开发:比如Windows操作系统的控件库,可以通过建造者自定义组合各种控件的位置、大小和样式。
XML文档生成器:通过建造者可以逐步构建复杂的XML文档结构,控制每个元素的添加顺序和属性设置。
数据库调度:可以用建造者提供一系列选项,如驱动程序、主机地址、端口等,按配置连接指定的数据库。
网络编程:可以根据需求动态配置HTTP、FTP或其他请求的细节,如URL、header信息、参数等。
五,建造者模式的优缺点
建造者模式的优点:
无需修改原有代码结构,可以灵活配置构建的过程。
封装性好,客户只需要关注如何组合,不需要关注组合的实现细节。
更加精细地控制对象的构建过程。
易于测试,由于建造过程是清晰的,所以更容易编写单元测试用例进行测试。
建造者模式的缺点:
对象创建过程的分解,使得代码量很大,且可读性差。
当对象的属性和组件特别多时,构建对象的代码会很复杂。
如果在对象初始化过程中就确定了所有属性,容易导致过早绑定。
六,代码实战
Demo1:模拟汉堡的制作过程
#include <iostream>
#include <string>
class Burger {
public:
void setBurgerType(const std::string& type) {
m_burgerType = type;
}
void setCheese(const bool cheese) {
m_cheese = cheese;
}
void setPickles(const bool pickles) {
m_pickles = pickles;
}
void setMayonnaise(const bool mayonnaise) {
m_mayonnaise = mayonnaise;
}
std::string getBurgerType() const {
return m_burgerType;
}
std::string getCheese() const {
return m_cheese ? "Cheese" : "No cheese";
}
std::string getPickles() const {
return m_pickles ? "Pickles" : "No pickles";
}
std::string getMayonnaise() const {
return m_mayonnaise ? "Mayonnaise" : "No mayonnaise";
}
private:
std::string m_burgerType;
bool m_cheese;
bool m_pickles;
bool m_mayonnaise;
};
class BurgerBuilder {
public:
BurgerBuilder() {
m_burger = new Burger();
}
BurgerBuilder* setBurgerType(const std::string& type) {
m_burger->setBurgerType(type);
return this;
}
BurgerBuilder* addCheese() {
m_burger->setCheese(true);
return this;
}
BurgerBuilder* addPickles() {
m_burger->setPickles(true);
return this;
}
BurgerBuilder* addMayonnaise() {
m_burger->setMayonnaise(true);
return this;
}
Burger* build() {
return m_burger;
}
private:
Burger* m_burger;
};
int main() {
Burger* burger = BurgerBuilder()
.setBurgerType("Chicken Burger")
->addCheese()
->addPickles()
->build();
std::cout << "Burger Type: " << burger->getBurgerType() << std::endl;
std::cout << "Cheese: " << burger->getCheese() << std::endl;
std::cout << "Pickles: " << burger->getPickles() << std::endl;
std::cout << "Mayonnaise: " << burger->getMayonnaise() << std::endl;
delete burger;
return 0;
}
运行结果:
Burger Type: Chicken Burger
Cheese: Cheese
Pickles: Pickles
Mayonnaise: No mayonnaise
Demo2:模拟台式机的组装过程
#include <iostream>
#include <string>
using namespace std;
// Product
class Computer {
public:
void setCPU(const std::string& cpu) {
cpu_ = cpu;
}
void setRAM(const std::string& ram) {
ram_ = ram;
}
void setStorage(const std::string& storage) {
storage_ = storage;
}
void displayInfo() const {
std::cout << "Computer Configuration:"
<< "\nCPU: " << cpu_
<< "\nRAM: " << ram_
<< "\nStorage: " << storage_ << "\n\n";
}
private:
string cpu_;
string ram_;
string storage_;
};
// Builder
class Builder {
public:
virtual void buildCPU() = 0;
virtual void buildRAM() = 0;
virtual void buildStorage() = 0;
virtual Computer getResult() = 0;
};
// ConcreteBuilder
class GamingComputerBuilder : public Builder {
private:
Computer computer_;
public:
void buildCPU() override {
computer_.setCPU("Gaming CPU");
}
void buildRAM() override {
computer_.setRAM("16GB DDR4");
}
void buildStorage() override {
computer_.setStorage("1TB SSD");
}
Computer getResult() override {
return computer_;
}
};
// Director
class ComputerDirector {
public:
void construct(Builder& builder) {
builder.buildCPU();
builder.buildRAM();
builder.buildStorage();
}
};
int main() {
GamingComputerBuilder gamingBuilder;
ComputerDirector director;
director.construct(gamingBuilder);
Computer gamingComputer = gamingBuilder.getResult();
gamingComputer.displayInfo();
return 0;
}
运行结果:
Computer Configuration:
CPU: Gaming CPU
RAM: 16GB DDR4
Storage: 1TB SSD
七,参考阅读
https://gist.github.com/pazdera/1121152
https://www.geeksforgeeks.org/builder-design-pattern/
https://www.tutorialspoint.com/design_pattern/builder_pattern.html
https://rust-unofficial.github.io/patterns/patterns/creational/builder.html