<摘要>
建造者模式是一种创建型设计模式,通过将复杂对象的构建过程分解为多个步骤,使相同的构建过程能够创建不同的表示形式。本文从背景起源、核心概念、设计意图等角度深入解析该模式,结合电脑组装、文档生成等实际案例展示其实现方式,通过UML图和代码演示详细说明构建过程,最后总结其优缺点和适用场景。
<解析>
建造者模式深度解析:构建复杂对象的艺术
1. 背景与核心概念
1.1 历史背景与发展脉络
建造者模式(Builder Pattern)最早由著名的计算机科学家Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides(四人被称为"Gang of Four",简称GoF)在他们1994年出版的经典著作《设计模式:可复用面向对象软件的基础》中提出。这本书系统性地总结了23种经典设计模式,建造者模式是其中之一,属于创建型模式类别。
在软件开发的早期阶段,开发者常常面临复杂对象的创建问题。特别是当对象需要多个组成部分,且这些部分有不同的组合方式时,传统的构造函数或工厂方法会变得异常复杂。举个例子,创建一个复杂的"房屋"对象可能需要设置墙壁、屋顶、门窗、地板等多个属性,而且这些属性可能有不同的材质、颜色和风格组合。
在建造者模式出现之前,常见的解决方案有两种:
- 使用重叠构造器:提供多个构造函数,每个构造函数接受不同数量的参数。这种方法会导致构造函数数量爆炸,代码难以阅读和维护。
// 重叠构造器示例 - 不推荐的方式
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) { /*...*/ }// 更多构造函数...
};
- 使用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)负责具体构建步骤,但不知道整体构建流程;最终的产品(Product)则通过一步步的构建过程组装而成。
1.3 与其他创建型模式的比较
为了更好地理解建造者模式的独特价值,让我们将其与其他创建型模式进行对比:
模式 | 目的 | 适用场景 |
---|---|---|
建造者模式 | 分步骤构建复杂对象 | 对象有复杂的内部结构,需要不同的表示 |
工厂方法模式 | 创建单一类型的对象 | 不关心对象的具体类,只需要一个对象 |
抽象工厂模式 | 创建相关对象家族 | 需要一组相互关联的对象 |
原型模式 | 通过克隆现有对象来创建新对象 | 创建成本较高,且与现有对象相似 |
建造者模式的独特之处在于它关注构建过程,而不仅仅是最终对象。它通过一步步的构建过程,最终组装成完整的对象,这对于需要多个步骤且步骤顺序重要的复杂对象特别有用。
2. 设计意图与考量
2.1 核心设计目标
建造者模式的设计意图可以从以下几个角度理解:
-
分离构建与表示:这是建造者模式最核心的目标。它将复杂对象的构建过程(如何构建)与其表示(构建什么)分离开来,使得同样的构建过程可以创建不同的表示。
-
精细化控制构建过程:通过将构建过程分解为一系列步骤,可以更精细地控制对象的构建过程。指挥者决定了构建的顺序,而具体建造者决定了每一步的具体实现。
-
避免"重叠构造器"反模式:建造者模式消除了需要多个参数不同构造函数的必要性,避免了构造函数数量爆炸的问题。
-
保证对象的一致性:在对象完全构建完成之前,它不会暴露给客户端,这保证了对象在使用时总是处于一致的状态。
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 适用场景
- 创建复杂对象:当对象需要多个步骤构建,且这些步骤可能有不同实现时。
- 构建过程需要不同表示:当同样的构建过程需要产生不同表示的结果时。
- 需要精细控制构建过程:当需要精确控制对象的构建过程和组成部分时。
- 避免构造函数污染:当避免使用多个参数不同的构造函数时。
2.3.2 不适用场景
- 简单对象创建:如果对象很简单,不需要多个构建步骤,直接使用构造函数更合适。
- 构建步骤变化很大:如果不同产品的构建步骤差异很大,建造者模式的抽象可能变得复杂。
- 产品接口不稳定:如果产品接口经常变化,建造者接口也需要相应变化,增加维护成本。
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 -WallTARGET = 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
-------------------------
代码解说:
在这个例子中,我们通过建造者模式实现了电脑组装系统:
-
产品:
Computer
类代表要组装的电脑,包含CPU、内存等组件。 -
抽象建造者:
ComputerBuilder
接口定义了组装电脑所需的各个步骤。 -
具体建造者:
GamingComputerBuilder
和OfficeComputerBuilder
分别实现了游戏电脑和办公电脑的具体组装细节。 -
指挥者:
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>
=========================
生成纯文本格式信函:
文档内容:
=========================
标题: === 正式信函 ===
章节: --- 收件人 ---
段落: 尊敬的客户:
段落: 感谢您使用我们的服务...
页脚: *** 公司名称 ***
=========================
代码解说:
在这个文档生成器例子中,我们展示了建造者模式的另一个应用场景:
-
同样的构建过程,不同的表示:指挥者
DocumentDirector
提供了两种文档构建流程(报告和信函),但通过不同的建造者(HTML和纯文本),产生了不同格式的表示。 -
构建过程的复用:
constructReport()
和constructLetter()
方法定义了两种文档结构,这些结构可以用于任何实现了DocumentBuilder
接口的建造者。 -
扩展性:如果需要支持新的文档格式(如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 运行结果与解说
运行程序后,输出结果如下:
制作汉堡套餐:
套餐内容:主食: 巨无霸汉堡饮料: 可乐配餐: 薯条, 苹果派
=========================
制作鸡肉套餐:
套餐内容:主食: 香辣鸡腿堡饮料: 雪碧配餐: 鸡块, 沙拉
=========================
代码解说:
这个快餐点餐系统的例子展示了建造者模式的另一个特点:
-
固定流程,可变内容:指挥者
MealDirector
定义了固定的套餐构建流程(先主食,再饮料,最后配餐),但不同的具体建造者提供了不同的内容实现。 -
产品组成的复杂性:产品
Meal
包含了不同类型的组成部分(字符串主食、字符串饮料、字符串列表配餐),建造者模式很好地处理了这种复杂性。 -
易于扩展:如果需要添加新的套餐类型(如素食套餐),只需要添加新的具体建造者,无需修改现有代码。
这个例子体现了建造者模式在处理"复杂对象"创建时的优势,特别是当对象由多个部分组成,且这些部分需要不同的实现时。
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 建造者模式的优势
-
分离关注点:将复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。
-
精细控制构建过程:将构建过程分解为一系列步骤,指挥者控制构建顺序,建造者控制具体实现。
-
代码可读性和维护性:避免了重叠构造器的问题,代码更加清晰易懂。
-
开闭原则:增加新的具体建造者无需修改现有代码,符合开闭原则。
5.2 建造者模式的缺点
-
增加代码复杂度:需要定义多个类(产品、抽象建造者、具体建造者、指挥者),增加了系统的复杂度。
-
产品差异大时不适用:如果产品之间的差异很大,建造者模式的优势不明显。
-
产品修改影响大:如果产品接口经常变化,建造者接口也需要相应变化。
5.3 适用场景总结
-
创建复杂对象:当对象需要多个部分组合,且构建过程复杂时。
-
构建过程需要不同表示:当同样的构建过程需要产生不同结果时。
3.需要精细控制构建过程:当需要精确控制对象的构建步骤和顺序时。
- 避免构造函数污染:当避免使用多个参数不同的构造函数时。
建造者模式是创建型模式中的重要成员,它通过分离构建过程与表示,提供了创建复杂对象的灵活解决方案。在实际开发中,结合流畅接口等现代编程技巧,可以使建造者模式更加 powerful 和易用。