設計模式(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
英文地址,請注明出處:http://en.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;而是…