设计模式——建造者设计模式(创建型)
摘要
本文详细介绍了建造者设计模式,这是一种创建型设计模式,旨在将复杂对象的构建过程与其表示分离,便于创建不同表示。文中阐述了其设计意图,如隐藏创建细节、提升代码可读性和可维护性,并通过构建电脑的示例加以说明。接着展示了建造者模式的结构,包括抽象建造者、具体建造者、指挥者和产品角色。还提供了 Java 实现示例,分析了其特点与好处,探讨了适用场景,包括适合与不适合的情况,并通过风控请求类的实战示例展示了其优势。最后,提出了结合责任链与建造者构建不同风险场景组合的思考方向。
1. 建造者设计模式定义
建造者模式是一种创建型设计模式,用于将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。定义总结:将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的对象。
1.1. 🧠 设计意图:
- 屏蔽对象创建的细节,客户端不需要知道构建步骤。
- 提高代码的可读性、可维护性。
- 不同建造者构建不同产品,便于扩展。
1.2. 📝 举例说明:
假设我们要构建一台电脑,电脑由 CPU、内存、硬盘等多个部分组成,如果不使用建造者模式,每次创建电脑都要手动写出完整流程,容易出错。使用建造者模式之后,我们可以:
- 定义不同的建造者(如
GamingComputerBuilder
、OfficeComputerBuilder
)。 - 使用统一的构建流程(由
Director
控制)生成对应配置的电脑对象。
2. 建造者设计模式结构
2.1. 建造者设计模式类图
建造者模式包含如下角色:
- Builder:抽象建造者
- ConcreteBuilder:具体建造者
- Director:指挥者
- Product:产品角色
2.2. 建造者设计模式时序图
3. 建造者设计模式实现方式
3.1. ✅ Java 实现示例(以构建一台电脑为例)
3.1.1. 产品类(Product)
public class Computer {private String cpu;private String memory;private String storage;// setterpublic void setCpu(String cpu) { this.cpu = cpu; }public void setMemory(String memory) { this.memory = memory; }public void setStorage(String storage) { this.storage = storage; }@Overridepublic String toString() {return "Computer [CPU=" + cpu + ", Memory=" + memory + ", Storage=" + storage + "]";}
}
3.1.2. 抽象建造者(Builder)
public interface ComputerBuilder {void buildCpu();void buildMemory();void buildStorage();Computer getComputer();
}
3.1.3. 具体建造者(ConcreteBuilder)
public class GamingComputerBuilder implements ComputerBuilder {private Computer computer = new Computer();@Overridepublic void buildCpu() {computer.setCpu("Intel i9");}@Overridepublic void buildMemory() {computer.setMemory("32GB DDR5");}@Overridepublic void buildStorage() {computer.setStorage("2TB SSD");}@Overridepublic Computer getComputer() {return computer;}
}
3.1.4. 指挥者(Director)
public class ComputerDirector {public Computer construct(ComputerBuilder builder) {builder.buildCpu();builder.buildMemory();builder.buildStorage();return builder.getComputer();}
}
3.1.5. 客户端(Client)
public class Main {public static void main(String[] args) {ComputerBuilder builder = new GamingComputerBuilder();ComputerDirector director = new ComputerDirector();Computer computer = director.construct(builder);System.out.println(computer);}
}
3.2. 🧠 建造者设计模式特点与好处
- 将构建过程与表示解耦,提高灵活性
- 更适合构建复杂对象或多步骤构建的对象
- 客户端无需关心细节,适配不同场景(如游戏电脑、办公电脑)
4. 建造者设计模式适合场景
4.1. ✅ 建造者设计模式适合的场景
场景描述 | 举例说明 |
对象构建复杂,包含多个可选或必须参数、或有构建顺序要求 | 如一台电脑需设置 CPU、内存、硬盘等,并按照一定顺序构造 |
同一个构建过程,生成不同表现形式的对象 | 如根据配置构建不同风格的 UI 界面,或生成不同类型的报表(PDF/HTML/Excel) |
希望封装构建过程,将构建与表示分离,构建逻辑复杂 | 如报文协议构建、SQL 语句构建、复杂 HTTP 请求构建等 |
需要一步一步构建对象,各步骤可自由组合/复用 | 如订单生成流程、商品 SKU 属性配置流程等 |
希望在创建对象时不直接暴露其创建逻辑,而是通过统一接口进行构建 | Spring 中的 |
4.2. ❌ 不适合使用建造者模式的场景
场景描述 | 原因说明 |
1️⃣ 对象结构简单,参数少或构建过程简单 | 使用构造函数或静态工厂方法即可,无需引入复杂建造者类 |
2️⃣ 对象变化频繁,构建逻辑变化快且不可复用 | 每次变化都要修改建造者结构,维护成本高 |
3️⃣ 需要一次性创建大量简单对象 | 建造者模式对象构建慢,效率低,适合复杂对象 |
4️⃣ 创建对象不涉及多个步骤或步骤之间没有顺序依赖 | 可以直接使用构造器重载或 |
4.3. ✅ 示例对比
4.3.1. ✔ 适合建造者模式的示例
User user = new UserBuilder().setName("张三").setAge(30).setEmail("zhangsan@example.com").build();
4.3.2. ❌ 不适合建造者模式的示例(对象简单)
User user = new User("张三", 30); // 直接构造函数更简单
5. 建造者设计模式实战示例
以下是一个 建造者设计模式在 Spring 项目中用于金融风控场景 的实战示例,场景描述为:构建一个复杂的风控请求对象 RiskRequest,该对象包含多个字段、可能依赖外部配置、构建顺序有要求,并且需要灵活组合。
5.1. 🌟 场景背景
在金融风控系统中,需要对接多个风控策略服务,而每次调用前都需构造复杂的风控请求体 RiskRequest
,其字段包括用户、交易、设备、地理位置、行为历史等,字段较多、部分字段可选、构建过程复杂。
5.2. 🧱 建造者模式结构设计
5.2.1. 风控请求类 RiskRequest
public class RiskRequest {private String userId;private String ip;private String deviceId;private String location;private BigDecimal amount;private String channel;private Map<String, Object> extension;// 私有构造器,避免外部直接 newprivate RiskRequest() {}// Getter 省略public static class Builder {private final RiskRequest request = new RiskRequest();public Builder userId(String userId) {request.userId = userId;return this;}public Builder ip(String ip) {request.ip = ip;return this;}public Builder deviceId(String deviceId) {request.deviceId = deviceId;return this;}public Builder location(String location) {request.location = location;return this;}public Builder amount(BigDecimal amount) {request.amount = amount;return this;}public Builder channel(String channel) {request.channel = channel;return this;}public Builder extension(Map<String, Object> ext) {request.extension = ext;return this;}public RiskRequest build() {// 可添加参数校验逻辑return request;}}
}
5.2.2. Spring 风控服务中使用
@Service
public class RiskEngineService {public RiskResult assessRisk(TransactionContext ctx) {RiskRequest request = new RiskRequest.Builder().userId(ctx.getUserId()).ip(ctx.getIp()).deviceId(ctx.getDeviceId()).location(ctx.getLocation()).amount(ctx.getAmount()).channel("APP").extension(Map.of("loginTime", ctx.getLoginTime())).build();// 调用远程风控引擎return remoteRiskClient.invoke(request);}
}
5.3. ✅ 优势分析
维度 | 表现 |
可读性 |
链式构建清晰易读 |
可维护性 | 字段变更不影响业务逻辑代码,只需改 builder |
可扩展性 | 可灵活扩展参数、做默认值处理 |
解耦 | 业务逻辑和构建逻辑分离,保持 Service 层简洁 |
5.4. 🔄 延伸:结合 Lombok 简化构建(若字段不太复杂)
@Data
@Builder
public class RiskRequest {private String userId;private String ip;private String deviceId;private String location;private BigDecimal amount;private String channel;private Map<String, Object> extension;
}
然后直接用:
RiskRequest request = RiskRequest.builder().userId("u123").ip("192.168.1.1").amount(new BigDecimal("999.00")).build();
6. 建造者设计模式思考
6.1. 结合责任链+建造者构建不同风险场景组合?
结合 责任链(Chain of Responsibility)+ 建造者模式(Builder Pattern),可以优雅地解决金融风控场景中 构建复杂请求对象时的多变逻辑组合问题。
6.1.1. 📌 背景场景
你在构建 RiskRequest
时,可能存在如下 变化点:
- 不同业务线(信贷、支付、转账)关注字段不同;
- 不同渠道(APP、小程序、Web)获取数据来源不同;
- 某些字段需要动态计算(如 IP 解析地理位置);
- 某些字段需要兜底或配置中心取默认值。
这些都很适合通过责任链机制 按模块职责划分建造逻辑,结合建造者模式,按步骤组合构建一个完整对象。
6.1.2. 🧱 设计结构图
RiskRequestBuilder├── RiskBuildHandlerA(构建用户信息)├── RiskBuildHandlerB(构建设备信息)├── RiskBuildHandlerC(构建交易信息)├── RiskBuildHandlerD(构建位置信息)└── RiskBuildHandlerX(扩展字段...)
6.1.3. ✅ 风控请求实体 + Builder
public class RiskRequest {private String userId;private String ip;private String location;private BigDecimal amount;private Map<String, Object> ext;private RiskRequest() {}public static class Builder {private final RiskRequest request = new RiskRequest();public Builder userId(String userId) {request.userId = userId; return this;}public Builder ip(String ip) {request.ip = ip; return this;}public Builder location(String location) {request.location = location; return this;}public Builder amount(BigDecimal amount) {request.amount = amount; return this;}public Builder ext(Map<String, Object> ext) {request.ext = ext; return this;}public RiskRequest build() {return request;}}
}
6.1.4. ✅ 定义责任链接口
public interface RiskRequestBuilderHandler {void handle(RiskRequest.Builder builder, RiskContext context);
}
6.1.5. ✅ 各构建器实现类(每个负责构建一部分)
@Component
public class UserInfoBuilderHandler implements RiskRequestBuilderHandler {public void handle(RiskRequest.Builder builder, RiskContext context) {builder.userId(context.getUserId());}
}@Component
public class IpInfoBuilderHandler implements RiskRequestBuilderHandler {public void handle(RiskRequest.Builder builder, RiskContext context) {builder.ip(context.getIp());}
}@Component
public class LocationBuilderHandler implements RiskRequestBuilderHandler {public void handle(RiskRequest.Builder builder, RiskContext context) {// 可通过 IP 反查地址builder.location("Shanghai"); // 示例}
}
6.1.6. ✅ 构建入口类(责任链容器)
@Component
public class RiskRequestDirector {@Autowiredprivate List<RiskRequestBuilderHandler> handlers;public RiskRequest build(RiskContext context) {RiskRequest.Builder builder = new RiskRequest.Builder();for (RiskRequestBuilderHandler handler : handlers) {handler.handle(builder, context);}return builder.build();}
}
✅ Spring 会自动注入所有 RiskRequestBuilderHandler
,实现责任链顺序处理(可用 @Order
控制顺序)
6.1.7. ✅ 风控服务中使用
@Service
public class RiskService {@Autowiredprivate RiskRequestDirector requestDirector;public RiskResult runRisk(TransactionContext ctx) {RiskContext riskContext = convert(ctx);RiskRequest request = requestDirector.build(riskContext);return remoteRiskClient.invoke(request);}
}
6.2. 🌟 责任链+建造者总结
点 | 描述 |
解耦构建逻辑 | 每个字段构建逻辑拆分为独立模块 |
符合开闭原则 | 新增构建字段不影响已有代码 |
动态适配 | 可结合配置中心动态选择构建器链 |
更适合扩展 | 支持多业务场景、策略组装、Mock 等 |
6.3. 🔧 可扩展方向
- 支持责任链动态配置(按业务线、渠道等匹配);
- 风控字段来源插件化(支持 SPI 加载数据构建器);
- 构建失败字段日志审计(异常处理链);
- 抽象为风控建模平台中的组件。
博文参考
- 建造者设计模式(生成器模式)
- 创建型 - 生成器(Builder) | Java 全栈知识体系
- 4. 建造者模式 — Graphic Design Patterns
相关文章:

设计模式——建造者设计模式(创建型)
摘要 本文详细介绍了建造者设计模式,这是一种创建型设计模式,旨在将复杂对象的构建过程与其表示分离,便于创建不同表示。文中阐述了其设计意图,如隐藏创建细节、提升代码可读性和可维护性,并通过构建电脑的示例加以说…...
AWS WebRTC:获取ICE服务地址(part 3):STUN服务和TURN服务的作用
STUN服务和TURN服务的作用: 服务全称作用是否中继流量适用场景STUNSession Traversal Utilities for NAT 协助设备发现自己的公网地址(srflx candidate) ❌ 不中继,仅辅助NAT 穿透成功时使用TURNTraversal Using Relays around N…...

使用Yolov8 训练交通标志数据集:TT100K数据集划分
使用Yolov8 训练交通标志数据集:TT100K数据集划分(一) 一、数据集下载二、划分数据集三、目录放置 一、数据集下载 官方网址:TT100K 数据集对比 源码如下: def classes(filedir):with open(filedir) as f:classes …...

NLP学习路线图(十三):正则表达式
在自然语言处理(NLP)的浩瀚宇宙中,原始文本数据如同未经雕琢的璞玉。而文本预处理,尤其是其中至关重要的正则表达式技术,正是将这块璞玉转化为精美玉器的核心工具集。本文将深入探讨正则表达式在NLP文本预处理中的原理…...
[VMM]现代 CPU 中用于加速多级页表查找的Page‐Table Entry原理
现代 CPU 中用于加速多级页表查找的Page‐Table Entry原理 摘要:以下从背景、结构、查找流程、一致性与性能影响等方面,详细介绍现代 CPU 中用于加速多级页表查找的 Page-Walk Cache(也称 Walker Cache 或 Page‐Table Entry Cache࿰…...

javaweb-maven以及http协议
1.maven坐标: 坐标是资源的唯一标识,通过该坐标可以唯一定位资源位置; 2.坐标的组成: groupId:定义当前项目隶书的组织名称; artifactId:定义当前maven项目名称 version:定义项目版本 3.依…...

华为OD机试真题—— 最少数量线段覆盖/多线段数据压缩(2025A卷:100分)Java/python/JavaScript/C++/C语言/GO六种最佳实现
2025 A卷 100分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析; 并提供Java、python、JavaScript、C++、C语言、GO六种语言的最佳实现方式! 2025华为OD真题目录+全流程解析/备考攻略/经验分享 华为OD机试真题《最少数量线段覆盖/多线段数…...

C语言创意编程:用趣味实例玩转基础语法(2)
文章目录 0. 前言1. 📊 动态条形图1.1 程序效果展示1.2 完整代码解析1.3 关键技术详解1.3.1 Unicode字符应用1.3.2 函数封装思想1.3.3 输入处理1.3.4 跨平台考虑 2. 🔤 字母金字塔2.1 程序效果展示2.2 完整代码解析2.3 关键技术详解2.3.1 嵌套循环结构2.…...
关于近期中国移动民用家庭网络,新增的UDP网络限制。
在近期中国移动在全国一定范围普及新的打击 “PCDN、P2P、HY/HY2” 等流氓网络应用的技术方案,并接入在 “省/州” 的边界网关路由上。 根据遥测数据的具体研究分析,且本人曾非常生气的详细质询过,移动城域网管理人员,可以确认该技…...

OpenCV CUDA模块图像处理------颜色空间处理之GPU 上对两张带有 Alpha 通道的图像进行合成操作函数alphaComp()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 该函数用于在 GPU 上对两张带有 Alpha 通道的图像进行合成操作。支持多种常见的 Alpha 合成模式(Porter-Duff 合成规则)&…...

OpenWebUI(1)源码学习构建
1. 前言 通过docker镜像拉取安装就不介绍了,官方的命令很多。本节主要撸一撸源码,所以,本地构建 2. 技术框架和启动环境 后端python,前端svelte 环境要求:python > 3.11 ,Node.js > 20.10 3. 源…...

npm error Cannot find module ‘negotiator‘ 的处理
本想运行npm create vuelatest,但提示: npm error code MODULE_NOT_FOUND npm error Cannot find module negotiator npm error Require stack: npm error - C:\Users\Administrator\AppData\Roaming\nvm\v18.16.1\node_modules\npm\node_modules\tuf-j…...

爬虫入门指南-某专利网站的专利数据查询并存储
免责声明 本教程仅用于教育目的,演示如何合法获取公开专利数据。在实际操作前,请务必: 1. 仔细阅读目标网站的robots.txt文件和服务条款 2. 控制请求频率,避免对服务器造成负担 3. 仅获取和使用公开数据 4. 不用于商业用途或…...

SQL(Database Modifications)
目录 Insertion Specifying Attributes in INSERT Adding Default Values(缺省值) Inserting Many Tuples Creating a Table Using the SELECT INTO Statement Deletion Example: Deletion Semantics of Deletion Updates Example: Update Sev…...

【android bluetooth 案例分析 04】【Carplay 详解 2】【Carplay 连接之手机主动连车机】
1. 背景 在【android bluetooth 案例分析 04】【Carplay 详解 1】【CarPlay 在车机侧的蓝牙通信原理与角色划分详解】中我们从整理上介绍了车机中 carplay 相关基础概念。 本节 将详细分析 iphone手机主动 连接 车机carplay 这一过程。 先回顾一下 上一节, carpla…...
maven离线将jar包导入到本地仓库中
想将本地的 jnetpcap.jar 包安装到 Maven 的本地仓库中,以便在项目中通过如下依赖方式引用。 <dependency><groupId>org.jnetpcap</groupId><artifactId>jnetpcap...

【仿muduo库实现并发服务器】实现时间轮定时器
实现时间轮定时器 1.时间轮定时器原理2.项目中实现目的3.实现功能3.1构造定时任务类3.2构造时间轮定时器每秒钟往后移动添加定时任务刷新定时任务取消定时任务 4.完整代码 1.时间轮定时器原理 时间轮定时器的原理类似于时钟,比如现在12点,定一个3点的闹…...
Conda更换镜像源教程:加速Python包下载
Conda更换镜像源教程:加速Python包下载 为什么要更换conda镜像源? Conda作为Python的包管理和环境管理工具,默认使用的是国外镜像源,在国内下载速度往往较慢。通过更换为国内镜像源,可以显著提高包下载速度ÿ…...
蓝桥杯 盗墓分赃2
原题目链接 问题描述 在一个探险者的团队中,小明和小红是合作的盗墓贼。 他们成功盗取了一座古墓中的宝藏,包括 n 件不同重量的珍贵文物和黄金,第 i 件宝藏的重量为 ai。 现在,他们希望公平地分配这些宝藏,使得小明…...
深度解读 Qwen3 大语言模型的关键技术
一、模型架构设计 Qwen3 延续了当前主流大型语言模型的 Transformer 架构,并在此基础上进行了多项增强设计,包含特殊的 Transformer 变体、位置编码机制改进、混合专家 (MoE) 技术引入,以及支持多模态和双重思考模式的新特性。 1. Transformer 基础架构与增强 基础架构:…...
使用 mysqldump 获取 MySQL 表的完整创建 DDL
要获取 MySQL 中某个表的完整创建 DDL(仅结构,不含数据),可以使用 mysqldump 工具的以下命令: 基本命令格式 bash mysqldump -h [主机名] -u [用户名] -p --no-data --single-transaction --routines --triggers --…...

day15 leetcode-hot100-28(链表7)
2. 两数相加 - 力扣(LeetCode) 1.模拟 思路 最核心的一点就是将两个链表模拟为等长,不足的假设为0; (1)设置一个新链表newl来代表相加结果。 (2)链表1与链表2相加,具…...
阿里云云效对接SDK获取流水线制品
参考文档: API旧版 企业令牌 https://help.aliyun.com/zh/yunxiao/developer-reference/api-reference API新版 个人令牌 https://help.aliyun.com/zh/yunxiao/developer-reference/api-reference-standard-proprietary API 个人令牌 https://www.alibabacloud.com…...
Qt 相关 编译流程及交叉编译 部署所遇到的问题总结-持续更新
准备环境和工具 1、主机环境 ubuntu20 2、交叉编译器 gcc-linaro-6.3.1…arm-linux-gnuebihf 3、QT5源码包qt-5.11.3_sources 下载qt-5.11.3的包,自己想办法下载 网盘啥的 都ok,再访问下载目录就可以显示了。 Index of /archive/qt 4、依赖库安装 sudo …...
前端面经 DNSxieyi1
域名解析协议 域名转为目标IP地址 两种方式 1 递归查询 A请求B B一定会告诉IP 2迭代查询 A请求B 如果B无能 ,B会告诉A如何获得改内容,但是B自己不会发出请求1 步骤 1.检查浏览器DNS 2.没有命中继续查询操作系统的DNS缓存 3.查询本地域名服务器&…...
如何通过ES实现SQL风格的查询?
一、Spring项目集成方案 添加依赖(pom.xml): <dependency><groupId>co.elastic.clients</groupId><artifactId>elasticsearch-java</artifactId><version>8.12.0</version> </dependency> <dependency><…...

知识图谱:重构认知的智能革命
在数字经济的浪潮中,知识图谱正悄然掀起一场认知革命。它不仅是技术的迭代,更是人类从“数据依赖”迈向“知识驱动”的里程碑。当谷歌用知识图谱优化搜索引擎、银行用它穿透复杂的金融欺诈网络、医院用它辅助癌症诊疗时,这项技术已悄然渗透到…...
【计算机网络】4网络层①
这篇笔记讲IPv4和IPv6。 为了解决“IP地址耗尽”问题,有三种措施: ①CIDR(延长IPv4使用寿命) ②NAT(延长IPv4使用寿命) ③IPv6(从根本上解决IP地址耗尽问题) IPv6 在考研中考查频率较低,但需掌握基础概念以防冷门考点,重点结合数据报格式和与 IPv4 的对比记忆。…...

MATLAB中的table数据类型:高效数据管理的利器
MATLAB中的table数据类型:高效数据管理的利器 什么是table数据类型? MATLAB中的table是一种用于存储列向数据的数据类型,它将不同类型的数据组织在一个表格结构中,类似于电子表格或数据库表。自R2013b版本引入以来,t…...

Dropout 在大语言模型中的应用:以 GPT 和 BERT 为例
引言 大型语言模型(LLMs)如 GPT(生成式预训练 Transformer)和 BERT(双向编码器表示 Transformer)通过其强大的语言理解和生成能力,彻底改变了自然语言处理(NLP)领域。然…...