模式动机与定义
复杂的对象由多个部分组成,建造者模式吧部件和其组装过程分开,一步步创建复杂的对象。建造者返还给客户端的是一个已经建造完毕的完整产品对象,而用户无须关心该对象所包含的属性以及它们的组装方式。
建造者模式允许用户通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部具体的构建细节。
#模式结构与分析
UML分析
角色:
- Builder:抽象建造者。定义了产品的创建和返回方法
- ConcreteBuilder:具体建造者 。抽象建造者的具体实现
- Director:指挥者。隔离客户与生产过程,负责控制产品的生成过程
- Product:产品角色。负责生产的各类产品
模式实例与解析
假设我们有一个产品类族AbstractProduct
这个产品类族有4个核心的模块A、B、C、D。不同的子产品实例如果要实际使用,则必须按照一定的顺序依次构造这4个子模块;但是,不同的子产品的构造顺序可能是不同的。
如果我们还使用工厂模式来构造,那么工厂内部必须要为每个不同的子类来确定模块的建造顺序。而且,可能有部分的子模块构造顺序是相同的,这样会造成代码的冗余,因为相同的构造顺序被写了多次。此时,需要引入建造者模式,把具有相同构造顺序的不同子模块放到一个建造者中进行建造。外层再添加一个Director,这是和用户的交互接口,用户实际使用这个类来获取实际的产品。
代码实例:
#include <iostream>
#include <memory>
class AbstractProduct { // 抽象产品
public:
// 产品有4个不同的顺序,但是使用前要按照特定的顺序构造A、B、C、D模块
virtual void setPartA() = 0;
virtual void setPartB() = 0;
virtual void setPartC() = 0;
virtual void setPartD() = 0;
virtual void show() = 0;
};
class AbstractBuilder { // 抽象构造者
public:
virtual std::shared_ptr<AbstractProduct> build(std::shared_ptr<AbstractProduct> product) = 0;
};
class Product1 : public AbstractProduct { // 具体产品1,有自己特有的构造顺序
public:
void setPartA() override {
std::cout << "Product 1 sets part A\n";
}
void setPartB() override {
std::cout << "Product 1 sets part B\n";
}
void setPartC() override {
std::cout << "Product 1 sets part C\n";
}
void setPartD() override {
std::cout << "Product 1 sets part D\n";
}
void show() override {
std::cout << "Product1 OK\n";
}
};
class Product2 : public AbstractProduct { // 具体产品2,有自己的构造顺序
public:
void setPartA() override {
std::cout << "Product 2 sets part A\n";
}
void setPartB() override {
std::cout << "Product 2 sets part B\n";
}
void setPartC() override {
std::cout << "Product 2 sets part C\n";
}
void setPartD() override {
std::cout << "Product 2 sets part D\n";
}
void show() override {
std::cout << "Product2 OK\n";
}
};
class Builder1 : public AbstractBuilder { // 建造者1,用于建造和产品1顺序相同产品的建造者
public:
std::shared_ptr<AbstractProduct> build(std::shared_ptr<AbstractProduct> product) {
product->setPartA();
product->setPartD();
product->setPartC();
product->setPartB();
return product;
}
};
class Builder2 : public AbstractBuilder { // 建造者2,用于建造和产品2顺序相同产品的建造者
public:
std::shared_ptr<AbstractProduct> build(std::shared_ptr<AbstractProduct> product) {
product->setPartB();
product->setPartD();
product->setPartA();
product->setPartC();
return product;
}
};
class Director { // 根据需求返回不同的产品
public:
std::shared_ptr<AbstractProduct> getProduct(int id) {
switch (id)
{
case 1:
m_spBuilder = std::make_shared<Builder1>();
m_spProduct = m_spBuilder->build(std::make_shared<Product1>());
break;
case 2:
m_spBuilder = std::make_shared<Builder2>();
m_spProduct = m_spBuilder->build(std::make_shared<Product2>());
break;
default:
m_spProduct = nullptr;
break;
}
return m_spProduct;
}
private:
std::shared_ptr<AbstractBuilder> m_spBuilder;
std::shared_ptr<AbstractProduct> m_spProduct;
};
int main() {
Director dir;
auto p1 = dir.getProduct(1);
p1->show();
std::cout << "-----------------\n";
auto p2 = dir.getProduct(2);
p2->show();
return 0;
}
输出:
Product 1 sets part A
Product 1 sets part D
Product 1 sets part C
Product 1 sets part B
Product1 OK
-----------------
Product 2 sets part B
Product 2 sets part D
Product 2 sets part A
Product 2 sets part C
Product2 OK
总结
优势
- 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
- 用户使用不同的具体建造者即可得到不同的产品对象。
- 可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
- 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则”。
劣势
- 如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
使用环境
- 需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。
- 需要生成的产品对象的属性相互依赖,需要指定其生成顺序
- 对象的创建过程独立于创建该对象的类
- 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。