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

<摘要>
建造者模式是一种创建型设计模式,通过将复杂对象的构建过程分解为多个步骤,使相同的构建过程能够创建不同的表示形式。本文从背景起源、核心概念、设计意图等角度深入解析该模式,结合电脑组装、文档生成等实际案例展示其实现方式,通过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 -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
-------------------------

代码解说

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

  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 和易用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/100324.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/100324.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

移动端触摸事件与鼠标事件的触发机制详解

移动端触摸事件与鼠标事件的触发机制详解 在移动端开发中&#xff0c;我们经常会遇到一个现象&#xff1a;一次简单的触摸操作&#xff0c;不仅会触发touch系列事件&#xff0c;还会触发一系列mouse事件&#xff0c;最终甚至会触发click事件。这其实是浏览器为了兼容传统桌面端…

如何科学评估CMS系统性能优化效果?

为什么要评估性能优化效果&#xff1f; 在投入时间精力优化CMS系统后&#xff0c;很多开发者只凭"感觉"判断网站变快了&#xff0c;但这种主观判断往往不可靠。科学评估性能优化效果可以帮助我们&#xff1a; 量化优化成果&#xff1a;用数据证明优化的价值发现潜在问…

中控平台数据监控大屏

中控平台数据监控大屏前言&#xff1a;什么是数据大屏&#xff1f; 数据大屏就像是一个"数字仪表盘"&#xff0c;把复杂的数据用图表、动画等方式直观展示出来。想象一下汽车的仪表盘&#xff0c;能让你一眼看到速度、油量、转速等信息——数据大屏也是这个原理&…

【Vue2手录13】路由Vue Router

一、Vue Router 基础概念与核心原理 1.1 路由本质与核心要素 本质定义&#xff1a;路由是URL路径与页面组件的对应关系&#xff0c;通过路径变化控制视图切换&#xff0c;实现单页应用&#xff08;SPA&#xff09;的无刷新页面切换。核心三要素&#xff1a; router-link&#x…

【Git】零基础入门:配置与初始操作实战指南

目录 1.前言 插播一条消息~ 2.正文 2.1概念 2.2安装与配置 2.3基础操作 2.3.1创建本地仓库 2.3.2配置Git 2.3.3认识工作区&#xff0c;暂存区&#xff0c;版本库 2.3.4版本回退 2.3.5撤销修改 2.3.6删除文件 3.小结 1.前言 在 Java 开发场景中&#xff0c;团队协…

CAD多面体密堆积_圆柱体试件3D插件

插件介绍 CAD多面体密堆积_圆柱体试件3D插件可在AutoCAD内基于重力堆积算法在圆柱体容器内进行多面体的密堆积三维建模。插件采取堆积可视化交互界面&#xff0c;可观察多面体颗粒的堆积动态&#xff0c;并可采用鼠标进行多面体位置的局部微调。插件可设置重力堆积模拟时长参数…

机器学习-模型调参、超参数优化

模型调参 手工超参数微调 以一个好的baseline开始&#xff0c;即&#xff1a;在一些高质量的工具包中的默认设置&#xff0c;论文中的值调一个值&#xff0c;重新训练这个模型来观察变化重复很多次获得对以下的insight&#xff1a; 1、哪个超参数重要 2、模型对超参数的敏感度是…

STM32 单片机开发 - I2C 总线

一、IIC(I2C) 线的作用UART总线 PC端(CPU) <----------> 开发板(STM32U575RIT6)IIC总线 主控芯片(STM32U575RIT6) <---------> 传感器驱动芯片(SHT20/SI7006空气温湿度传感器)二、I2C 总线的概念图 1 I2C 总线示意图图 2 多主机多从机模式示意图I2C 总…

Redis 数据结构源码剖析(SDS、Dict、Skiplist、Quicklist、Ziplist)

Redis 数据结构源码剖析&#xff08;SDS、Dict、Skiplist、Quicklist、Ziplist&#xff09;1. 前言 Redis 的高性能与丰富数据结构密切相关。 核心数据结构包括&#xff1a; SDS&#xff08;Simple Dynamic String&#xff09;&#xff1a;字符串底层实现。Dict&#xff08;哈希…

无人机图传系统的功能解析和技术实现原理

无人机图传系统要将机载摄像头捕捉到的画面以尽可能低的时延、尽可能高的清晰度、稳定可靠地送达地面操作员或指挥中心&#xff0c;进而驱动现场行动。为此&#xff0c;核心功能可以从四个维度来解构&#xff1a;实时性、画质与稳定性、覆盖与冗余、以及安全协同。实时性要求在…

微服务网关的bug

从你提供的Eureka控制台信息来看&#xff0c;SPRINGCLOUD-PRODUCT已成功注册到Eureka&#xff0c;且状态为UP&#xff08;实例地址localhost:springcloud-product:8082&#xff09;&#xff0c;排除了“服务未注册”“实例离线”的基础问题。但仍报“负载均衡无可用服务”&…

LeetCode:2.字母异位词分组

目录 1.字母异位词分组 1.字母异位词分组 对于这道题来说&#xff0c;关键的地方在于字母异位词他们排序后的字符串完全相等&#xff0c;所以我们可以通过哈希表来建设一个字符串和其排序相同的字符串数组的映射关系 class Solution { public:vector<vector<string>…

SwiftData3 一剑封喉:WWDC25 的“数据剑谱”精讲,让 Core Data 老侠原地退休

文章目录每日一句正能量一、开场白&#xff1a;老兵的隐痛二、SwiftData3 新剑谱总览三、亮剑&#xff1a;30 行代码搭一个「跨端秒级同步」的收藏夹1. 铸剑&#xff1a;声明模型2. 开锋&#xff1a;初始化容器3. 出招&#xff1a;SwiftUI7 直接绑四、进阶剑气&#xff1a;Char…

微服务-nacos服务中心

单体与微服务 单体架构&#xff1a;项目所有功能都在一个 war 包 /jar 包里&#xff0c;像商城的订单、库存、会员、支付等服务&#xff0c;都打包在一起&#xff0c;部署在 Tomcat 服务器&#xff0c;数据存在 MySQL。 优点&#xff1a;开发简单&#xff0c;易于理解和维护&am…

嵌入式硬件——IMX6ULL 裸机LED点亮实验

一. 实验准备 基于正点原子 IMX6ULL-Mini 开发板&#xff0c;实现 LED 周期性闪烁功能&#xff0c;需完成环境搭建与硬件原理确认两大核心准备工作。 1.1 开发环境搭建 需在Windows和Ubuntu中安装工具&#xff0c;确保文件传输、交叉编译、代码编辑功能正常。1.1.1 跨系统文件传…

深度学习之PyTorch基本使用(一)

一、PyTorch简介与安装1.核心概念PyTorch 是一款 Python 深度学习框架&#xff0c;其核心是张量&#xff08;Tensor&#xff09; —— 元素为同一种数据类型的多维矩阵&#xff0c;以 “类” 的形式封装&#xff0c;内置了张量运算、处理等方法&#xff0c;是深度学习中数据存储…

SQLAlchemy -> Base.metadata.create_all(engine )详解

目录 一、核心作用 二、是否每次运行项目都会执行&#xff1f; 1. ​​典型场景​​&#xff08;推荐&#xff09; 2. ​​需要避免的情况​​ 三、最佳实践建议 1. ​​生产环境​​ 2. ​​开发/测试环境​​ 四、常见问题解答 Q1: 如果表结构改了&#xff0c;creat…

C++异步任务处理与消息可靠性保障指南:从基础到实战

在当今多核处理器普及的时代&#xff0c;程序性能和响应能力的提升成为开发者面临的核心课题。无论是高频交易系统的毫秒级响应需求、实时游戏引擎的流畅交互体验&#xff0c;还是网络服务器的高并发处理能力&#xff0c;异步编程都已成为突破性能瓶颈的关键技术[1]。作为高性能…

LazyForEach性能优化:解决长列表卡顿问题

本文将深入解析HarmonyOS中LazyForEach的工作原理、性能优势、实战优化技巧及常见问题解决方案&#xff0c;帮助你构建流畅的长列表体验。 1. LazyForEach 核心优势与原理 LazyForEach 是鸿蒙ArkUI框架中为高性能列表渲染设计的核心组件&#xff0c;其核心设计思想基于动态加载…

Spring Boot 全栈优化:服务器、数据、缓存、日志的场景应用!

Spring Boot以其“开箱即用”闻名&#xff0c;但默认配置往往在高并发场景下成为瓶颈&#xff1a;Tomcat线程堵塞、数据库连接耗尽、缓存命中率低下、日志洪水般淹没磁盘。想象一个电商微服务&#xff0c;峰值流量下响应迟钝&#xff0c;用户流失——这不是宿命&#xff0c;而是…