文章目录

  • 建造者模式简介
  • 建造者模式结构
  • 建造者模式代码实例
    • 定义产品类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. …