深入淺出設計模式——創建型模式之建造者模式 Builder

文章目錄

  • 建造者模式簡介
  • 建造者模式結構
  • 建造者模式代碼實例
    • 定義產品類House
    • 定義建造者
      • 定義抽象建造者AbstractBuilder
      • 定義具體建造者
    • 定義指揮者
    • 客戶端代碼示例
    • 運行結果
  • 建造者模式總結

代碼倉庫
建一棟房子總共分幾步?建造者模式告訴你答案!
“把大象裝冰箱,總共分幾步?”
“三步。第一步,打開冰箱門;第二步,把大象裝進冰箱;第三步,把冰箱門關上。”

Jungle活了這20多年,全靠這個笑話活著! 把大象裝冰箱竟然只需要三步?那到底是怎么把大象裝進冰箱呢?你問我,我問誰?再說,我也不關心這個呀!這……來點實際的吧,如果Jungle要建一棟房子,總共分幾步?本文的建造者模式將聲情并茂地向您娓娓道來……

建造者模式簡介

建造者模式用于創建過程穩定,但配置多變的對象。在《設計模式》一書中的定義是:將一個復雜的構建與其表示相分離,使得同樣的構建過程可以創建不同的表示。

建造者模式將客戶端與包含多個部件的復雜對象的創建過程分離,客戶端不必知道復雜對象的內部組成方式與裝配方式(就好像Jungle不知道到底是如何把大象裝進冰箱一樣),只需知道所需建造者的類型即可。

建造者模式定義:
將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。

“同樣的構建過程可以創建不同的表示”??這句話是什么意思呢?想象一下,建造一棟房子,建造過程無非都是打地基、筑墻、安裝門窗等過程,但不同的客戶可能希望不同的風格或者過程,最終建造出來的房子當然就呈現不同的風格啦!

經典的“建造者-指揮者”模式現在已經不太常用了,現在建造者模式主要用來通過鏈式調用生成不同的配置。比如我們要制作一杯珍珠奶茶。它的制作過程是穩定的,除了必須要知道奶茶的種類和規格外,是否加珍珠和是否加冰是可選的。

使用建造者模式的好處是不用擔心忘了指定某個配置,保證了構建過程是穩定的。在 OkHttp、Retrofit 等著名框架的源碼中都使用到了建造者模式。

在這里插入圖片描述

建造者模式結構

在這里插入圖片描述
建造者模式UML類圖如下:

在這里插入圖片描述

建造者模式代碼實例

考慮這樣一個場景,如下圖:
在這里插入圖片描述
Jungle想要建造一棟簡易的房子(地板、墻和天花板),兩個工程師帶著各自的方案找上門來,直接給Jungle看方案和效果圖。
猶豫再三,Jungle最終選定了一位工程師……交房之日,Jungle滿意的看著建好的房子,
開始思考:這房子究竟是怎么建成的呢?這地板、墻和天花板是怎么建造的呢?
工程師笑著說:“It’s none of your business”

UML圖如下:
在這里插入圖片描述

定義產品類House

House是本實例中的產品,具有floor、wall和roof三個屬性。

class House {
public:House() {}void setFloor(string iFloor) {this->floor = iFloor;}void setWall(string iWall) {this->wall = iWall;}void setRoof(string iRoof) {this->roof = iRoof;}// 打印House信息void printfHouseInfo() {// this->floor 是一個 std::string 類型,而C語言函數 printf 無法直接輸出 std::string 類型。// 使用 .c_str() 就能將 std::string 轉換為一個 const char*(C風格字符串) ,供 printf 使用。printf("Floor:%s\t\n", this->floor.c_str());printf("Wall:%s\t\n", this->wall.c_str());printf("Roof:%s\t\n", this->roof.c_str());}private:string floor;string wall;string roof;
};

定義建造者

定義抽象建造者AbstractBuilder

// 抽象建造者AbstractBuilder
class AbstractBuilder {
public:AbstractBuilder() : house(std::make_unique<House>()) {}virtual ~AbstractBuilder() = default;  // 使用默認析構函數即可AbstractBuilder(const AbstractBuilder&) = delete;AbstractBuilder& operator=(const AbstractBuilder&) = delete;virtual void buildFloor() = 0;virtual void buildWall() = 0;virtual void buildRoof() = 0;virtual std::unique_ptr<House> getHouse() {return std::move(house);  // 轉移所有權}protected:std::unique_ptr<House> house;
};

定義具體建造者

// 具體建造者ConcreteBuilderA
// 子類無需再定義getHouse()和析構函數,因為基類已經完成這些任務。
class ConcreteBuilderA: public AbstractBuilder {
public:ConcreteBuilderA() { printf("ConcreteBuilderA\n"); }void buildFloor() override { house->setFloor("Floor_A"); }void buildWall() override { house->setWall("Wall_A"); }void buildRoof() override { house->setRoof("Roof_A"); }
};// 具體建造者ConcreteBuilderB
class ConcreteBuilderB: public AbstractBuilder {
public:ConcreteBuilderB() { printf("ConcreteBuilderB\n"); }void buildFloor() override { house->setFloor("Floor_B"); }void buildWall() override { house->setWall("Wall_B"); }void buildRoof() override { house->setRoof("Roof_B"); }
};

定義指揮者

// 指揮者Director
class Director {
public:Director(): builder(nullptr) {}// Builder的生命周期應由調用方自己管理,不能由Director刪除,否則可能造成未知問題。~Director() = default;  Director(const Director&) = delete;Director& operator=(const Director&) = delete;void setBuilder(AbstractBuilder* iBuilder) {this->builder = iBuilder;}std::unique_ptr<House> construct() {builder->buildFloor();builder->buildWall();builder->buildRoof();return builder->getHouse();  // 返回智能指針}private:AbstractBuilder* builder;
};

客戶端代碼示例

#include "BuilderPattern.h"int main() {// 抽象建造者AbstractBuilder* builder = nullptr;// 指揮者 (生命周期在main中)Director director;  // 指定具體建造者Abuilder = new ConcreteBuilderA();director.setBuilder(builder);// house的所有權轉移給調用方std::unique_ptr<House> houseA = director.construct();houseA->printfHouseInfo();// 注意:house內存已被外部接管,不應由builder釋放delete builder;  // 此時builder的析構函數應避免刪除house// delete houseA;   // 由調用方手動釋放house內存// 指定具體建造者B (這里應改成ConcreteBuilderB)builder = new ConcreteBuilderB();director.setBuilder(builder);std::unique_ptr<House> houseB = director.construct();houseB->printfHouseInfo();delete builder;  // 同上// delete houseB;   // 調用方負責釋放內存return 0;
}

運行結果

在這里插入圖片描述

建造者模式總結

從客戶端代碼可以看到,客戶端只需指定具體建造者,并作為參數傳遞給指揮者,通過指揮者即可得到結果。**客戶端無需關心House的建造方法和具體流程。如果要更換建造風格,只需更換具體建造者即可,不同建造者之間并無任何關聯,方便替換。**從代碼優化角度來看,其實可以不需要指揮者Director的角色,而直接把construct方法放入具體建造者當中。

在這里插入圖片描述

之后我會持續更新,如果喜歡我的文章,請記得一鍵三連哦,點贊關注收藏,你的每一個贊每一份關注每一次收藏都將是我前進路上的無限動力 !!!↖(▔▽▔)↗感謝支持!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/91501.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/91501.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/91501.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

OpenVLA: 論文閱讀 -- 開源視覺-語言-行動模型

更多內容&#xff1a;XiaoJ的知識星球 目錄OpenVLA&#xff1a;開源視覺-語言-行動模型1. 介紹2. 相關工作1&#xff09;視覺條件語言模型&#xff08;Visually-Conditioned Language Models&#xff09;2&#xff09;通用型機器人策略&#xff08;Generalist Robot Policies&a…

JavaWeb(蒼穹外賣)--學習筆記15(分頁查詢PageHelper)

前言 終于開始學習做項目了&#xff0c;本篇文章是學習B站黑馬程序員蒼穹外賣的學習筆記&#x1f4d1;。我的學習路線是Java基礎語法-JavaWeb-做項目&#xff0c;管理端的功能學習完之后&#xff0c;就進入到了用戶端微信小程序的開發&#xff0c;這篇文章來看看分頁查詢&#…

金融專題|某跨境支付機構:以榫卯企業云平臺 VPC 功能保障業務主體安全

作者&#xff1a;SmartX 金融團隊 金融機構在信息化建設時面臨諸多數據合規要求&#xff0c;例如&#xff1a;不同業務區域之間互相隔離、數據庫僅能由關聯的應用服務器訪問、僅有特定的服務器允許被外網訪問等。對此&#xff0c;某跨境支付機構以 SmartX 榫卯企業云平臺構建私…

Win10下python環境變量呼出微軟應用商店

以下是三種徹底解決 Windows 10 的 CMD 中運行 python 命令彈出應用商店問題的方法??方法一&#xff1a;調整環境變量優先級?-或者直接刪除微軟應用商店的環境變量%USERPROFILE%\AppData\Local\Microsoft\WindowsApp???操作步驟??打開系統環境變量設置&#xff08;右鍵…

字節跳動“扣子”(Coze)開源:AI智能體生態的技術革命

&#xff08;以下借助 DeepSeek-R1 輔助整理&#xff09; 在2025年7月26日的深夜&#xff0c;GitHub上悄然出現的兩個倉庫——Coze Studio和Coze Loop&#xff0c;在48小時內狂攬超過9,000顆Star。字節跳動以Apache 2.0許可證將自家AI智能體平臺的核心技術徹底開源。 “當所有人…

Camx-usecase ID和pipeline的匹配源碼解讀

組件關系整體流程&#xff1a;camxhal3.cpp:704 open()camxhal3.cpp:1423 configure_streams()chxextensionmodule.cpp:2810 InitializeOverrideSessionchxusecaseutils.cpp:850 GetMatchingUsecase()chxadvancedcamerausecase.cpp:4729 Initialize()chxadvancedcamerausecase.…

日志管理進入「對話式」時代:日志易MCP Server落地實錄

01 背景&#xff1a;MCP協議介紹在AI蓬勃發展的當下&#xff0c;大型語言模型&#xff08;LLM&#xff09;雖展現出強大潛力&#xff0c;卻受困于與外部資源連接的難題。數據分散、接口繁雜&#xff0c;致使AI模型難以靈活對接本地資源與遠程服務&#xff0c;極大限制了其響應質…

django-3模型操作

from django.db import modelsclass Book(models.Model):title models.CharField(max_length200) # 書名author models.CharField(max_length100) # 作者publish_date models.DateField() # 出版日期price models.DecimalField(max_digits10, decimal_places2) # 價格s…

【繪制圖像輪廓】——圖像預處理(OpenCV)

目錄 1 什么是輪廓 2 尋找輪廓 2.1 mode參數 2.2 method參數 3 繪制輪廓 1 什么是輪廓 輪廓是一系列相連的點組成的曲線&#xff0c;代表了物體的基本外形。輪廓是連續的&#xff0c;邊緣不一定連續。輪廓是一個閉合的、封閉的形狀。 輪廓的作用&#xff1a; 形狀分析 目…

嵌入式 Linux 深度解析:架構、原理與工程實踐(增強版)

嵌入式 Linux 深度解析&#xff1a;架構、原理與工程實踐&#xff08;增強版&#xff09; 目錄嵌入式 Linux 深度解析&#xff1a;架構、原理與工程實踐&#xff08;增強版&#xff09;第一章 嵌入式 Linux 基礎概念1.1 定義與核心特征1.2 典型架構棧深度解析第二章 Linux 文件…

xcode swift項目運行、連接真機運行報錯,引入文件夾失敗

最近亂七八糟解決了很多報錯&#xff0c;看著記錄點吧 xcode版本&#xff1a;16 failed to emit precompiled header ‘/Users/yuqing/Library/Developer/Xcode/DerivedData/cloudspace-ios-ejldldcfhouqnretchuzoewmsqkg/Build/Intermediates.noindex/PrecompiledHeaders/spic…

[python][selenium] Web UI自動化8種頁面元素定位方式

測試工程師必備&#xff01;Selenium自動化測試全攻略 | 手寫POM框架數據驅動&#xff0c;輕松搞定UI自動化&#xff01;簡單的加個前置知識&#xff1a; 第一&#xff1a;webdriver.Chrome()這句話&#xff0c;通過WebDriver的構造方法&#xff0c;拿到瀏覽器驅動的對象&…

絲桿支撐座在電子裝配中的關鍵作用

絲桿支撐座是電子裝配過程中不可或缺的組件&#xff0c;主要用于支撐和固定絲桿&#xff0c;確保其穩定性和精度。在高速、高精度裝配場景中&#xff0c;絲桿支撐座的作用尤為突出。穩定性與精度保障&#xff1a;絲桿支撐座采用高品質鋼材制作&#xff0c;具有高剛性和高強度&a…

微信小程序頁面間通信的實現方式

微信小程序中頁面間的通信是指不同頁面之間的數據傳遞、狀態同步或交互操作&#xff0c;常見于多頁面協作場景。根據通信方向和場景不同&#xff0c;主要有以下幾種實現方式&#xff1a;一、基于頁面跳轉的參數傳遞1. 正向傳遞&#xff08;A頁面到B頁面&#xff09;通過URL參數…

uniapp開發微信小程序(新舊版本對比:授權手機號登錄、授權頭像和昵稱)

目錄標題授權手機號新舊版本核心差異對比強制使用新版的情況代碼實現方案特殊處理邏輯企業賬號要求最佳實踐建議授權頭像和昵稱新舊版本核心差異對比強制使用新版的情況代碼實現方案最佳實踐建議注意事項授權手機號 新舊版本核心差異對比 觸發方式 舊版&#xff08;2023年前&…

Java函數式編程之【Stream終止操作】【下】【三】【收集操作collect()與分組分區】【下游收集器】

分組收集器groupingBy()&#xff1a;groupingBy()收集器用于按條件對元素象進行分組&#xff0c;并將結果存儲在Map實例中。其作用與數據庫的SQL語句的group by的用法有異曲同工之妙。 分區收集器partitioningBy()&#xff1a;partitioningBy()可以看作是分組groupingBy()的特殊…

python設計模式-工廠模式

工廠模式的核心思想&#xff1a;封裝對象創建過程、解耦對象使用與創建 。示例代碼&#xff1a;from enum import Enum# 基類&#xff1a;人類 class Person:species Homo sapiensdef __init__(self, name):self.name namedef __str__(self):return f"{self.__class__._…

Rust:anyhow::Result 與其他 Result 類型轉換

當函數返回的不是 anyhow::Result 而是其他 Result 類型時&#xff08;如 std::io::Result、serde_json::Result 或自定義 Result&#xff09;&#xff0c;可通過以下方法統一處理錯誤類型&#xff0c;確保與 anyhow 兼容或實現錯誤傳播&#xff1a;&#x1f6e0;? 一、錯誤類…

PLC-梯形圖編程

1.位運算,比較 如&#xff1a;>,<,, 2.定時器 生成脈沖TP&#xff0c;常開觸點閉合觸發&#xff0c;賦值10秒時長&#xff0c;PT配置參數&#xff0c;ET運行時已PT計時 接通延時TON&#xff0c;常開觸點閉合觸發&#xff0c;延時10秒后賦值 關斷延時TOF&#xff0c;常開觸…

LLM學習筆記5——InstructGPT

系列文章目錄 參考文獻 參考文獻 參考文獻 參考視頻 文章目錄系列文章目錄前言目前大模型不同的技術流派與框架路線&#xff1a;1. ??BERT&#xff1a;Encoder-only架構????1&#xff09; 架構特點????2&#xff09; 訓練目標??3&#xff09; ????應用場景2. …