设计模式(C++)详解——建造者模式(1)

发布于:2025-09-14 ⋅ 阅读:(22) ⋅ 点赞:(0)

<摘要>
建造者模式是一种创建型设计模式,通过将复杂对象的构建过程分解为多个步骤,使相同的构建过程能够创建不同的表示形式。本文从背景起源、核心概念、设计意图等角度深入解析该模式,结合电脑组装、文档生成等实际案例展示其实现方式,通过UML图和代码演示详细说明构建过程,最后总结其优缺点和适用场景。


<解析>

建造者模式深度解析:构建复杂对象的艺术

1. 背景与核心概念

1.1 历史背景与发展脉络

建造者模式(Builder Pattern)最早由著名的计算机科学家Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides(四人被称为"Gang of Four",简称GoF)在他们1994年出版的经典著作《设计模式:可复用面向对象软件的基础》中提出。这本书系统性地总结了23种经典设计模式,建造者模式是其中之一,属于创建型模式类别。

在软件开发的早期阶段,开发者常常面临复杂对象的创建问题。特别是当对象需要多个组成部分,且这些部分有不同的组合方式时,传统的构造函数或工厂方法会变得异常复杂。举个例子,创建一个复杂的"房屋"对象可能需要设置墙壁、屋顶、门窗、地板等多个属性,而且这些属性可能有不同的材质、颜色和风格组合。

在建造者模式出现之前,常见的解决方案有两种:

  1. 使用重叠构造器:提供多个构造函数,每个构造函数接受不同数量的参数。这种方法会导致构造函数数量爆炸,代码难以阅读和维护。
// 重叠构造器示例 - 不推荐的方式
class House {
public:
    House(int walls) { /*...*/ }
    House(int walls, int doors) { /*...*/ }
    House(int walls, int doors, int windows) { /*...*/ }
    House(int walls, int doors, int windows, string roofType) { /*...*/ }
    // 更多构造函数...
};
  1. 使用setter方法:先创建对象,然后通过setter方法设置属性。这种方法虽然避免了构造函数爆炸,但会导致对象在完全构建之前处于不一致状态。
// 使用setter方法 - 对象可能处于不一致状态
House house;
house.setWalls(4);
// 此时house对象还不完整,但不能使用
house.setDoors(2);
house.setWindows(6);
// 现在才能使用

建造者模式的出现解决了这些问题,它将复杂对象的构建过程分离出来,使得同样的构建过程可以创建不同的表示,同时保证了对象在构建过程中的一致性。

1.2 核心概念与关键术语

建造者模式包含以下几个核心角色:

角色 职责描述 类比现实世界
产品(Product) 要创建的复杂对象,包含多个组成部分 一套完整的电脑系统
抽象建造者(Builder) 声明创建产品各个部分的抽象接口 电脑组装说明书的大纲
具体建造者(Concrete Builder) 实现Builder接口,完成产品各个部分的具体构建 按照说明书组装电脑的技术人员
指挥者(Director) 负责安排复杂对象的建造次序 电脑组装项目的项目经理

为了更好地理解这些角色之间的关系,让我们通过一个UML类图来可视化建造者模式的结构:

Director
-builder: Builder
+construct()
«interface»
Builder
+buildPartA()
+buildPartB()
+buildPartC()
+getResult() : Product
ConcreteBuilder
-product: Product
+buildPartA()
+buildPartB()
+buildPartC()
+getResult() : Product
Product
+parts: List
+addPart(part)
+show()

从上图可以看出,建造者模式的核心思想是分离构建过程与表示。指挥者(Director)负责控制构建过程,但不知道具体构建细节;建造者(Builder)负责具体构建步骤,但不知道整体构建流程;最终的产品(Product)则通过一步步的构建过程组装而成。

1.3 与其他创建型模式的比较

为了更好地理解建造者模式的独特价值,让我们将其与其他创建型模式进行对比:

模式 目的 适用场景
建造者模式 分步骤构建复杂对象 对象有复杂的内部结构,需要不同的表示
工厂方法模式 创建单一类型的对象 不关心对象的具体类,只需要一个对象
抽象工厂模式 创建相关对象家族 需要一组相互关联的对象
原型模式 通过克隆现有对象来创建新对象 创建成本较高,且与现有对象相似

建造者模式的独特之处在于它关注构建过程,而不仅仅是最终对象。它通过一步步的构建过程,最终组装成完整的对象,这对于需要多个步骤且步骤顺序重要的复杂对象特别有用。

2. 设计意图与考量

2.1 核心设计目标

建造者模式的设计意图可以从以下几个角度理解:

  1. 分离构建与表示:这是建造者模式最核心的目标。它将复杂对象的构建过程(如何构建)与其表示(构建什么)分离开来,使得同样的构建过程可以创建不同的表示。

  2. 精细化控制构建过程:通过将构建过程分解为一系列步骤,可以更精细地控制对象的构建过程。指挥者决定了构建的顺序,而具体建造者决定了每一步的具体实现。

  3. 避免"重叠构造器"反模式:建造者模式消除了需要多个参数不同构造函数的必要性,避免了构造函数数量爆炸的问题。

  4. 保证对象的一致性:在对象完全构建完成之前,它不会暴露给客户端,这保证了对象在使用时总是处于一致的状态。

2.2 设计考量因素

在实际应用建造者模式时,需要考虑以下几个因素:

2.2.1 构建过程的灵活性

建造者模式提供了很大的灵活性,但这种灵活性需要在设计时仔细考虑:

  • 步骤顺序的重要性:某些产品的构建步骤有严格的顺序要求(如建造房屋必须先打地基再建墙),而有些步骤顺序可以灵活调整。
  • 可选部件:产品中的某些部件可能是可选的,建造者模式需要能够处理这种情况。
  • 不同表示:同样的构建过程如何产生不同的产品表示。
2.2.2 接口设计的简洁性

建造者接口的设计需要平衡简洁性和表达能力:

// 过于细粒度的接口 - 不推荐
class OverlyDetailedBuilder {
public:
    virtual void buildScrew(int size) = 0;
    virtual void buildNut(int size) = 0;
    virtual void buildBolt(int size) = 0;
    // 数十个类似方法...
};

// 适当粒度的接口 - 推荐
class AppropriateBuilder {
public:
    virtual void buildFrame() = 0;      // 构建框架
    virtual void buildEngine() = 0;     // 安装发动机
    virtual void buildWheels() = 0;     // 安装车轮
    // 合理数量的方法...
};
2.2.3 与相关模式的协同

建造者模式经常与其他模式结合使用:

  • 与工厂模式结合:当需要创建多个相关的复杂对象时,可以使用抽象工厂模式创建不同的建造者。
  • 与组合模式结合:当产品本身是组合结构时,建造者可以用于逐步构建复杂的组合对象。
  • 与单例模式结合:如果某个具体建造者是无状态的,可以将其实现为单例。

2.3 适用场景与不适用场景

2.3.1 适用场景
  1. 创建复杂对象:当对象需要多个步骤构建,且这些步骤可能有不同实现时。
  2. 构建过程需要不同表示:当同样的构建过程需要产生不同表示的结果时。
  3. 需要精细控制构建过程:当需要精确控制对象的构建过程和组成部分时。
  4. 避免构造函数污染:当避免使用多个参数不同的构造函数时。
2.3.2 不适用场景
  1. 简单对象创建:如果对象很简单,不需要多个构建步骤,直接使用构造函数更合适。
  2. 构建步骤变化很大:如果不同产品的构建步骤差异很大,建造者模式的抽象可能变得复杂。
  3. 产品接口不稳定:如果产品接口经常变化,建造者接口也需要相应变化,增加维护成本。

3. 实例与应用场景

3.1 案例一:电脑组装系统

3.1.1 应用场景描述

假设我们需要一个电脑组装系统,可以组装不同类型的电脑(游戏电脑、办公电脑、服务器等)。每种电脑使用相同的基本组装步骤(安装CPU、内存、硬盘等),但具体组件不同。

3.1.2 完整代码实现

首先,我们定义产品类 - 电脑:

// product.h
#ifndef PRODUCT_H
#define PRODUCT_H

#include <string>
#include <vector>
#include <iostream>

// 电脑类 - 最终产品
class Computer {
public:
    void setCPU(const std::string& cpu) { m_cpu = cpu; }
    void setRAM(const std::string& ram) { m_ram = ram; }
    void setStorage(const std::string& storage) { m_storage = storage; }
    void setGPU(const std::string& gpu) { m_gpu = gpu; }
    void setMotherboard(const std::string& motherboard) { m_motherboard = motherboard; }
    
    void show() const {
        std::cout << "电脑配置:\n";
        std::cout << "  CPU: " << m_cpu << "\n";
        std::cout << "  内存: " << m_ram << "\n";
        std::cout << "  存储: " << m_storage << "\n";
        std::cout << "  显卡: " << m_gpu << "\n";
        std::cout << "  主板: " << m_motherboard << "\n";
        std::cout << "-------------------------\n";
    }

private:
    std::string m_cpu;
    std::string m_ram;
    std::string m_storage;
    std::string m_gpu;
    std::string m_motherboard;
};

#endif // PRODUCT_H

接下来,定义抽象建造者接口:

// builder.h
#ifndef BUILDER_H
#define BUILDER_H

#include "product.h"

// 抽象建造者接口
class ComputerBuilder {
public:
    virtual ~ComputerBuilder() {}
    
    virtual void buildCPU() = 0;
    virtual void buildRAM() = 0;
    virtual void buildStorage() = 0;
    virtual void buildGPU() = 0;
    virtual void buildMotherboard() = 0;
    
    virtual Computer getResult() = 0;
};

#endif // BUILDER_H

实现两个具体建造者:游戏电脑建造者和办公电脑建造者:

// concrete_builders.h
#ifndef CONCRETE_BUILDERS_H
#define CONCRETE_BUILDERS_H

#include "builder.h"

// 游戏电脑建造者
class GamingComputerBuilder : public ComputerBuilder {
public:
    GamingComputerBuilder() { m_computer = Computer(); }
    
    void buildCPU() override { m_computer.setCPU("Intel i9-12900K"); }
    void buildRAM() override { m_computer.setRAM("32GB DDR5"); }
    void buildStorage() override { m_computer.setStorage("2TB NVMe SSD"); }
    void buildGPU() override { m_computer.setGPU("NVIDIA RTX 4090"); }
    void buildMotherboard() override { m_computer.setMotherboard("Z690 Chipset"); }
    
    Computer getResult() override { return m_computer; }

private:
    Computer m_computer;
};

// 办公电脑建造者
class OfficeComputerBuilder : public ComputerBuilder {
public:
    OfficeComputerBuilder() { m_computer = Computer(); }
    
    void buildCPU() override { m_computer.setCPU("Intel i5-12400"); }
    void buildRAM() override { m_computer.setRAM("16GB DDR4"); }
    void buildStorage() override { m_computer.setStorage("512GB SSD"); }
    void buildGPU() override { m_computer.setGPU("Integrated Graphics"); }
    void buildMotherboard() override { m_computer.setMotherboard("B660 Chipset"); }
    
    Computer getResult() override { return m_computer; }

private:
    Computer m_computer;
};

#endif // CONCRETE_BUILDERS_H

实现指挥者类,负责控制构建过程:

// director.h
#ifndef DIRECTOR_H
#define DIRECTOR_H

#include "builder.h"

// 指挥者 - 负责控制构建过程
class ComputerDirector {
public:
    void setBuilder(ComputerBuilder* builder) {
        m_builder = builder;
    }
    
    void construct() {
        m_builder->buildMotherboard();
        m_builder->buildCPU();
        m_builder->buildRAM();
        m_builder->buildStorage();
        m_builder->buildGPU();
    }

private:
    ComputerBuilder* m_builder;
};

#endif // DIRECTOR_H

最后,实现主函数来演示使用:

// main.cpp
#include <iostream>
#include "director.h"
#include "concrete_builders.h"

int main() {
    // 创建指挥者和建造者
    ComputerDirector director;
    GamingComputerBuilder gamingBuilder;
    OfficeComputerBuilder officeBuilder;
    
    // 组装游戏电脑
    std::cout << "组装游戏电脑...\n";
    director.setBuilder(&gamingBuilder);
    director.construct();
    Computer gamingPC = gamingBuilder.getResult();
    gamingPC.show();
    
    // 组装办公电脑
    std::cout << "组装办公电脑...\n";
    director.setBuilder(&officeBuilder);
    director.construct();
    Computer officePC = officeBuilder.getResult();
    officePC.show();
    
    return 0;
}
3.1.3 Makefile 编译配置
# Makefile
CXX = g++
CXXFLAGS = -std=c++11 -Wall

TARGET = computer_builder
SOURCES = main.cpp
HEADERS = product.h builder.h concrete_builders.h director.h

$(TARGET): $(SOURCES) $(HEADERS)
	$(CXX) $(CXXFLAGS) -o $(TARGET) $(SOURCES)

clean:
	rm -f $(TARGET)

.PHONY: clean
3.1.4 编译与运行
# 编译
make

# 运行
./computer_builder
3.1.5 运行结果与解说

运行程序后,输出结果如下:

组装游戏电脑...
电脑配置:
  CPU: Intel i9-12900K
  内存: 32GB DDR5
  存储: 2TB NVMe SSD
  显卡: NVIDIA RTX 4090
  主板: Z690 Chipset
-------------------------
组装办公电脑...
电脑配置:
  CPU: Intel i5-12400
  内存: 16GB DDR4
  存储: 512GB SSD
  显卡: Integrated Graphics
  主板: B660 Chipset
-------------------------

代码解说

在这个例子中,我们通过建造者模式实现了电脑组装系统:

  1. 产品Computer类代表要组装的电脑,包含CPU、内存等组件。

  2. 抽象建造者ComputerBuilder接口定义了组装电脑所需的各个步骤。

  3. 具体建造者GamingComputerBuilderOfficeComputerBuilder分别实现了游戏电脑和办公电脑的具体组装细节。

  4. 指挥者ComputerDirector控制了电脑组装的流程顺序。

通过这种方式,我们实现了构建过程与表示的分离:同样的组装流程(指挥者控制的步骤顺序)可以创建不同类型的电脑(不同的表示)。

3.2 案例二:文档生成器

3.2.1 应用场景描述

假设我们需要一个文档生成系统,可以生成不同格式的文档(HTML、Markdown、Plain Text等)。每种格式的文档有相同的结构(标题、正文、页脚等),但具体实现方式不同。

3.2.2 完整代码实现

首先,定义文档产品类:

// document.h
#ifndef DOCUMENT_H
#define DOCUMENT_H

#include <string>
#include <iostream>

// 文档类 - 最终产品
class Document {
public:
    void addTitle(const std::string& title) { m_content += "标题: " + title + "\n"; }
    void addHeading(const std::string& heading) { m_content += "章节: " + heading + "\n"; }
    void addParagraph(const std::string& paragraph) { m_content += "段落: " + paragraph + "\n"; }
    void addFooter(const std::string& footer) { m_content += "页脚: " + footer + "\n"; }
    
    void show() const {
        std::cout << "文档内容:\n";
        std::cout << "=========================\n";
        std::cout << m_content;
        std::cout << "=========================\n";
    }
    
    const std::string& getContent() const { return m_content; }

private:
    std::string m_content;
};

#endif // DOCUMENT_H

定义抽象建造者接口:

// document_builder.h
#ifndef DOCUMENT_BUILDER_H
#define DOCUMENT_BUILDER_H

#include "document.h"

// 抽象文档建造者接口
class DocumentBuilder {
public:
    virtual ~DocumentBuilder() {}
    
    virtual void buildTitle(const std::string& title) = 0;
    virtual void buildHeading(const std::string& heading) = 0;
    virtual void buildParagraph(const std::string& paragraph) = 0;
    virtual void buildFooter(const std::string& footer) = 0;
    
    virtual Document getResult() = 0;
};

#endif // DOCUMENT_BUILDER_H

实现具体建造者:HTML文档建造者和纯文本文档建造者:

// concrete_document_builders.h
#ifndef CONCRETE_DOCUMENT_BUILDERS_H
#define CONCRETE_DOCUMENT_BUILDERS_H

#include "document_builder.h"

// HTML文档建造者
class HTMLDocumentBuilder : public DocumentBuilder {
public:
    HTMLDocumentBuilder() { m_document = Document(); }
    
    void buildTitle(const std::string& title) override {
        m_document.addTitle("<h1>" + title + "</h1>");
    }
    
    void buildHeading(const std::string& heading) override {
        m_document.addHeading("<h2>" + heading + "</h2>");
    }
    
    void buildParagraph(const std::string& paragraph) override {
        m_document.addParagraph("<p>" + paragraph + "</p>");
    }
    
    void buildFooter(const std::string& footer) override {
        m_document.addFooter("<footer>" + footer + "</footer>");
    }
    
    Document getResult() override { return m_document; }

private:
    Document m_document;
};

// 纯文本文档建造者
class PlainTextDocumentBuilder : public DocumentBuilder {
public:
    PlainTextDocumentBuilder() { m_document = Document(); }
    
    void buildTitle(const std::string& title) override {
        m_document.addTitle("=== " + title + " ===");
    }
    
    void buildHeading(const std::string& heading) override {
        m_document.addHeading("--- " + heading + " ---");
    }
    
    void buildParagraph(const std::string& paragraph) override {
        m_document.addParagraph(paragraph);
    }
    
    void buildFooter(const std::string& footer) override {
        m_document.addFooter("*** " + footer + " ***");
    }
    
    Document getResult() override { return m_document; }

private:
    Document m_document;
};

#endif // CONCRETE_DOCUMENT_BUILDERS_H

实现文档指挥者:

// document_director.h
#ifndef DOCUMENT_DIRECTOR_H
#define DOCUMENT_DIRECTOR_H

#include "document_builder.h"

// 文档指挥者
class DocumentDirector {
public:
    void setBuilder(DocumentBuilder* builder) {
        m_builder = builder;
    }
    
    void constructReport() {
        m_builder->buildTitle("季度报告");
        m_builder->buildHeading("引言");
        m_builder->buildParagraph("这是报告的引言部分...");
        m_builder->buildHeading("主要内容");
        m_builder->buildParagraph("这是报告的主要内容...");
        m_builder->buildFooter("报告生成于2024年1月");
    }
    
    void constructLetter() {
        m_builder->buildTitle("正式信函");
        m_builder->buildHeading("收件人");
        m_builder->buildParagraph("尊敬的客户:");
        m_builder->buildParagraph("感谢您使用我们的服务...");
        m_builder->buildFooter("公司名称");
    }

private:
    DocumentBuilder* m_builder;
};

#endif // DOCUMENT_DIRECTOR_H

主函数演示:

// main_document.cpp
#include <iostream>
#include "document_director.h"
#include "concrete_document_builders.h"

int main() {
    DocumentDirector director;
    HTMLDocumentBuilder htmlBuilder;
    PlainTextDocumentBuilder textBuilder;
    
    // 生成HTML格式报告
    std::cout << "生成HTML格式报告:\n";
    director.setBuilder(&htmlBuilder);
    director.constructReport();
    Document htmlReport = htmlBuilder.getResult();
    htmlReport.show();
    
    // 生成纯文本格式信函
    std::cout << "生成纯文本格式信函:\n";
    director.setBuilder(&textBuilder);
    director.constructLetter();
    Document textLetter = textBuilder.getResult();
    textLetter.show();
    
    return 0;
}
3.2.3 运行结果与解说

运行程序后,输出结果如下:

生成HTML格式报告:
文档内容:
=========================
标题: <h1>季度报告</h1>
章节: <h2>引言</h2>
段落: <p>这是报告的引言部分...</p>
章节: <h2>主要内容</h2>
段落: <p>这是报告的主要内容...</p>
页脚: <footer>报告生成于2024年1月</footer>
=========================
生成纯文本格式信函:
文档内容:
=========================
标题: === 正式信函 ===
章节: --- 收件人 ---
段落: 尊敬的客户:
段落: 感谢您使用我们的服务...
页脚: *** 公司名称 ***
=========================

代码解说

在这个文档生成器例子中,我们展示了建造者模式的另一个应用场景:

  1. 同样的构建过程,不同的表示:指挥者DocumentDirector提供了两种文档构建流程(报告和信函),但通过不同的建造者(HTML和纯文本),产生了不同格式的表示。

  2. 构建过程的复用constructReport()constructLetter()方法定义了两种文档结构,这些结构可以用于任何实现了DocumentBuilder接口的建造者。

  3. 扩展性:如果需要支持新的文档格式(如PDF、Word等),只需要实现新的具体建造者,无需修改指挥者或其他代码。

这个例子很好地演示了建造者模式如何"使得同样的构建过程可以创建不同的表示"。

3.3 案例三:快餐点餐系统

3.3.1 应用场景描述

考虑一个快餐店的点餐系统,顾客可以点不同类型的套餐(如汉堡套餐、鸡肉套餐等),每个套餐包含主食、饮料和配餐,但具体内容不同。

3.3.2 完整代码实现

定义套餐产品类:

// meal.h
#ifndef MEAL_H
#define MEAL_H

#include <string>
#include <vector>
#include <iostream>

// 套餐类 - 最终产品
class Meal {
public:
    void setMainItem(const std::string& item) { m_mainItem = item; }
    void setDrink(const std::string& drink) { m_drink = drink; }
    void addSide(const std::string& side) { m_sides.push_back(side); }
    
    void show() const {
        std::cout << "套餐内容:\n";
        std::cout << "  主食: " << m_mainItem << "\n";
        std::cout << "  饮料: " << m_drink << "\n";
        std::cout << "  配餐: ";
        for (size_t i = 0; i < m_sides.size(); ++i) {
            if (i > 0) std::cout << ", ";
            std::cout << m_sides[i];
        }
        std::cout << "\n";
        std::cout << "=========================\n";
    }

private:
    std::string m_mainItem;
    std::string m_drink;
    std::vector<std::string> m_sides;
};

#endif // MEAL_H

定义抽象建造者接口:

// meal_builder.h
#ifndef MEAL_BUILDER_H
#define MEAL_BUILDER_H

#include "meal.h"

// 抽象套餐建造者接口
class MealBuilder {
public:
    virtual ~MealBuilder() {}
    
    virtual void buildMainItem() = 0;
    virtual void buildDrink() = 0;
    virtual void buildSide() = 0;
    
    virtual Meal getResult() = 0;
};

#endif // MEAL_BUILDER_H

实现具体建造者:汉堡套餐建造者和鸡肉套餐建造者:

// concrete_meal_builders.h
#ifndef CONCRETE_MEAL_BUILDERS_H
#define CONCRETE_MEAL_BUILDERS_H

#include "meal_builder.h"

// 汉堡套餐建造者
class BurgerMealBuilder : public MealBuilder {
public:
    BurgerMealBuilder() { m_meal = Meal(); }
    
    void buildMainItem() override {
        m_meal.setMainItem("巨无霸汉堡");
    }
    
    void buildDrink() override {
        m_meal.setDrink("可乐");
    }
    
    void buildSide() override {
        m_meal.addSide("薯条");
        m_meal.addSide("苹果派");
    }
    
    Meal getResult() override { return m_meal; }

private:
    Meal m_meal;
};

// 鸡肉套餐建造者
class ChickenMealBuilder : public MealBuilder {
public:
    ChickenMealBuilder() { m_meal = Meal(); }
    
    void buildMainItem() override {
        m_meal.setMainItem("香辣鸡腿堡");
    }
    
    void buildDrink() override {
        m_meal.setDrink("雪碧");
    }
    
    void buildSide() override {
        m_meal.addSide("鸡块");
        m_meal.addSide("沙拉");
    }
    
    Meal getResult() override { return m_meal; }

private:
    Meal m_meal;
};

#endif // CONCRETE_MEAL_BUILDERS_H

实现指挥者:

// meal_director.h
#ifndef MEAL_DIRECTOR_H
#define MEAL_DIRECTOR_H

#include "meal_builder.h"

// 套餐指挥者
class MealDirector {
public:
    void setBuilder(MealBuilder* builder) {
        m_builder = builder;
    }
    
    void constructMeal() {
        m_builder->buildMainItem();
        m_builder->buildDrink();
        m_builder->buildSide();
    }

private:
    MealBuilder* m_builder;
};

#endif // MEAL_DIRECTOR_H

主函数演示:

// main_meal.cpp
#include <iostream>
#include "meal_director.h"
#include "concrete_meal_builders.h"

int main() {
    MealDirector director;
    BurgerMealBuilder burgerBuilder;
    ChickenMealBuilder chickenBuilder;
    
    // 制作汉堡套餐
    std::cout << "制作汉堡套餐:\n";
    director.setBuilder(&burgerBuilder);
    director.constructMeal();
    Meal burgerMeal = burgerBuilder.getResult();
    burgerMeal.show();
    
    // 制作鸡肉套餐
    std::cout << "制作鸡肉套餐:\n";
    director.setBuilder(&chickenBuilder);
    director.constructMeal();
    Meal chickenMeal = chickenBuilder.getResult();
    chickenMeal.show();
    
    return 0;
}
3.3.3 运行结果与解说

运行程序后,输出结果如下:

制作汉堡套餐:
套餐内容:
  主食: 巨无霸汉堡
  饮料: 可乐
  配餐: 薯条, 苹果派
=========================
制作鸡肉套餐:
套餐内容:
  主食: 香辣鸡腿堡
  饮料: 雪碧
  配餐: 鸡块, 沙拉
=========================

代码解说

这个快餐点餐系统的例子展示了建造者模式的另一个特点:

  1. 固定流程,可变内容:指挥者MealDirector定义了固定的套餐构建流程(先主食,再饮料,最后配餐),但不同的具体建造者提供了不同的内容实现。

  2. 产品组成的复杂性:产品Meal包含了不同类型的组成部分(字符串主食、字符串饮料、字符串列表配餐),建造者模式很好地处理了这种复杂性。

  3. 易于扩展:如果需要添加新的套餐类型(如素食套餐),只需要添加新的具体建造者,无需修改现有代码。

这个例子体现了建造者模式在处理"复杂对象"创建时的优势,特别是当对象由多个部分组成,且这些部分需要不同的实现时。

4. 高级主题与变体

4.1 流畅接口(Fluent Interface)实现

在现代C++中,建造者模式经常与流畅接口结合使用,提供更优雅的API:

// fluent_builder.h
#ifndef FLUENT_BUILDER_H
#define FLUENT_BUILDER_H

#include <string>
#include <iostream>

class Computer {
public:
    void setCPU(const std::string& cpu) { m_cpu = cpu; }
    void setRAM(const std::string& ram) { m_ram = ram; }
    void show() const {
        std::cout << "CPU: " << m_cpu << ", RAM: " << m_ram << "\n";
    }

private:
    std::string m_cpu;
    std::string m_ram;
};

// 流畅接口建造者
class FluentComputerBuilder {
public:
    FluentComputerBuilder& withCPU(const std::string& cpu) {
        m_computer.setCPU(cpu);
        return *this;
    }
    
    FluentComputerBuilder& withRAM(const std::string& ram) {
        m_computer.setRAM(ram);
        return *this;
    }
    
    Computer build() {
        return m_computer;
    }

private:
    Computer m_computer;
};

#endif // FLUENT_BUILDER_H

使用示例:

// 使用流畅接口建造者
FluentComputerBuilder builder;
Computer computer = builder.withCPU("Intel i7")
                          .withRAM("16GB DDR4")
                          .build();
computer.show();

流畅接口使得代码更加易读和流畅,类似于自然语言。

4.2 带有验证的建造者

在某些场景下,我们需要在构建过程中验证参数的有效性:

// validated_builder.h
#ifndef VALIDATED_BUILDER_H
#define VALIDATED_BUILDER_H

#include <string>
#include <stdexcept>

class ValidatedComputer {
public:
    void setCPU(const std::string& cpu) { 
        if (cpu.empty()) throw std::invalid_argument("CPU不能为空");
        m_cpu = cpu; 
    }
    // ... 其他setter方法

private:
    std::string m_cpu;
    // ... 其他字段
};

class ValidatedComputerBuilder {
public:
    ValidatedComputerBuilder& withCPU(const std::string& cpu) {
        m_computer.setCPU(cpu);
        return *this;
    }
    
    ValidatedComputer build() {
        // 构建前的最终验证
        if (/* 某些验证条件 */) {
            throw std::runtime_error("电脑配置无效");
        }
        return m_computer;
    }

private:
    ValidatedComputer m_computer;
};

#endif // VALIDATED_BUILDER_H

这种方式确保了最终产品的有效性。

5. 总结

5.1 建造者模式的优势

  1. 分离关注点:将复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。

  2. 精细控制构建过程:将构建过程分解为一系列步骤,指挥者控制构建顺序,建造者控制具体实现。

  3. 代码可读性和维护性:避免了重叠构造器的问题,代码更加清晰易懂。

  4. 开闭原则:增加新的具体建造者无需修改现有代码,符合开闭原则。

5.2 建造者模式的缺点

  1. 增加代码复杂度:需要定义多个类(产品、抽象建造者、具体建造者、指挥者),增加了系统的复杂度。

  2. 产品差异大时不适用:如果产品之间的差异很大,建造者模式的优势不明显。

  3. 产品修改影响大:如果产品接口经常变化,建造者接口也需要相应变化。

5.3 适用场景总结

  1. 创建复杂对象:当对象需要多个部分组合,且构建过程复杂时。

  2. 构建过程需要不同表示:当同样的构建过程需要产生不同结果时。

3.需要精细控制构建过程:当需要精确控制对象的构建步骤和顺序时。

  1. 避免构造函数污染:当避免使用多个参数不同的构造函数时。

建造者模式是创建型模式中的重要成员,它通过分离构建过程与表示,提供了创建复杂对象的灵活解决方案。在实际开发中,结合流畅接口等现代编程技巧,可以使建造者模式更加 powerful 和易用。