设计模式——建造者设计模式(创建型)

news2025/6/3 5:32:15

摘要

本文详细介绍了建造者设计模式,这是一种创建型设计模式,旨在将复杂对象的构建过程与其表示分离,便于创建不同表示。文中阐述了其设计意图,如隐藏创建细节、提升代码可读性和可维护性,并通过构建电脑的示例加以说明。接着展示了建造者模式的结构,包括抽象建造者、具体建造者、指挥者和产品角色。还提供了 Java 实现示例,分析了其特点与好处,探讨了适用场景,包括适合与不适合的情况,并通过风控请求类的实战示例展示了其优势。最后,提出了结合责任链与建造者构建不同风险场景组合的思考方向。

1. 建造者设计模式定义

建造者模式是一种创建型设计模式,用于将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。定义总结:将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的对象。

1.1. 🧠 设计意图:

  • 屏蔽对象创建的细节,客户端不需要知道构建步骤。
  • 提高代码的可读性、可维护性。
  • 不同建造者构建不同产品,便于扩展。

1.2. 📝 举例说明:

假设我们要构建一台电脑,电脑由 CPU、内存、硬盘等多个部分组成,如果不使用建造者模式,每次创建电脑都要手动写出完整流程,容易出错。使用建造者模式之后,我们可以:

  • 定义不同的建造者(如 GamingComputerBuilderOfficeComputerBuilder)。
  • 使用统一的构建流程(由 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;

    // setter
    public void setCpu(String cpu) { this.cpu = cpu; }
    public void setMemory(String memory) { this.memory = memory; }
    public void setStorage(String storage) { this.storage = storage; }

    @Override
    public 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();

    @Override
    public void buildCpu() {
        computer.setCpu("Intel i9");
    }

    @Override
    public void buildMemory() {
        computer.setMemory("32GB DDR5");
    }

    @Override
    public void buildStorage() {
        computer.setStorage("2TB SSD");
    }

    @Override
    public 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 中的 RestTemplateBuilderHttpClientBuilder

4.2. ❌ 不适合使用建造者模式的场景

场景描述

原因说明

1️⃣ 对象结构简单,参数少或构建过程简单

使用构造函数或静态工厂方法即可,无需引入复杂建造者类

2️⃣ 对象变化频繁,构建逻辑变化快且不可复用

每次变化都要修改建造者结构,维护成本高

3️⃣ 需要一次性创建大量简单对象

建造者模式对象构建慢,效率低,适合复杂对象

4️⃣ 创建对象不涉及多个步骤或步骤之间没有顺序依赖

可以直接使用构造器重载或 @Builder注解

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;

    // 私有构造器,避免外部直接 new
    private 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. ✅ 优势分析

维度

表现

可读性

.xxx().xxx()

链式构建清晰易读

可维护性

字段变更不影响业务逻辑代码,只需改 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 {

    @Autowired
    private 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 {

    @Autowired
    private 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

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

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

相关文章

使用Yolov8 训练交通标志数据集:TT100K数据集划分

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

NLP学习路线图(十三):正则表达式

在自然语言处理&#xff08;NLP&#xff09;的浩瀚宇宙中&#xff0c;原始文本数据如同未经雕琢的璞玉。而文本预处理&#xff0c;尤其是其中至关重要的正则表达式技术&#xff0c;正是将这块璞玉转化为精美玉器的核心工具集。本文将深入探讨正则表达式在NLP文本预处理中的原理…

javaweb-maven以及http协议

1.maven坐标&#xff1a; 坐标是资源的唯一标识&#xff0c;通过该坐标可以唯一定位资源位置&#xff1b; 2.坐标的组成&#xff1a; groupId:定义当前项目隶书的组织名称&#xff1b; artifactId&#xff1a;定义当前maven项目名称 version&#xff1a;定义项目版本 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. &#x1f4ca; 动态条形图1.1 程序效果展示1.2 完整代码解析1.3 关键技术详解1.3.1 Unicode字符应用1.3.2 函数封装思想1.3.3 输入处理1.3.4 跨平台考虑 2. &#x1f524; 字母金字塔2.1 程序效果展示2.2 完整代码解析2.3 关键技术详解2.3.1 嵌套循环结构2.…

OpenCV CUDA模块图像处理------颜色空间处理之GPU 上对两张带有 Alpha 通道的图像进行合成操作函数alphaComp()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 该函数用于在 GPU 上对两张带有 Alpha 通道的图像进行合成操作。支持多种常见的 Alpha 合成模式&#xff08;Porter-Duff 合成规则&#xff09;&…

OpenWebUI(1)源码学习构建

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

npm error Cannot find module ‘negotiator‘ 的处理

本想运行npm create vuelatest&#xff0c;但提示&#xff1a; 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…

爬虫入门指南-某专利网站的专利数据查询并存储

免责声明 本教程仅用于教育目的&#xff0c;演示如何合法获取公开专利数据。在实际操作前&#xff0c;请务必&#xff1a; 1. 仔细阅读目标网站的robots.txt文件和服务条款 2. 控制请求频率&#xff0c;避免对服务器造成负担 3. 仅获取和使用公开数据 4. 不用于商业用途或…

SQL(Database Modifications)

目录 Insertion Specifying Attributes in INSERT Adding Default Values&#xff08;缺省值&#xff09; 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 这一过程。 先回顾一下 上一节&#xff0c; carpla…

【仿muduo库实现并发服务器】实现时间轮定时器

实现时间轮定时器 1.时间轮定时器原理2.项目中实现目的3.实现功能3.1构造定时任务类3.2构造时间轮定时器每秒钟往后移动添加定时任务刷新定时任务取消定时任务 4.完整代码 1.时间轮定时器原理 时间轮定时器的原理类似于时钟&#xff0c;比如现在12点&#xff0c;定一个3点的闹…

day15 leetcode-hot100-28(链表7)

2. 两数相加 - 力扣&#xff08;LeetCode&#xff09; 1.模拟 思路 最核心的一点就是将两个链表模拟为等长&#xff0c;不足的假设为0&#xff1b; &#xff08;1&#xff09;设置一个新链表newl来代表相加结果。 &#xff08;2&#xff09;链表1与链表2相加&#xff0c;具…

​​知识图谱:重构认知的智能革命​

在数字经济的浪潮中&#xff0c;知识图谱正悄然掀起一场认知革命。它不仅是技术的迭代&#xff0c;更是人类从“数据依赖”迈向“知识驱动”的里程碑。当谷歌用知识图谱优化搜索引擎、银行用它穿透复杂的金融欺诈网络、医院用它辅助癌症诊疗时&#xff0c;这项技术已悄然渗透到…

【计算机网络】4网络层①

这篇笔记讲IPv4和IPv6。 为了解决“IP地址耗尽”问题,有三种措施: ①CIDR(延长IPv4使用寿命) ②NAT(延长IPv4使用寿命) ③IPv6(从根本上解决IP地址耗尽问题) IPv6 在考研中考查频率较低,但需掌握基础概念以防冷门考点,重点结合数据报格式和与 IPv4 的对比记忆。…

MATLAB中的table数据类型:高效数据管理的利器

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

Dropout 在大语言模型中的应用:以 GPT 和 BERT 为例

引言 大型语言模型&#xff08;LLMs&#xff09;如 GPT&#xff08;生成式预训练 Transformer&#xff09;和 BERT&#xff08;双向编码器表示 Transformer&#xff09;通过其强大的语言理解和生成能力&#xff0c;彻底改变了自然语言处理&#xff08;NLP&#xff09;领域。然…

gitLab 切换中文模式

点击【头像】--选择settings 选择【language】,选择中文&#xff0c;点击【保存】即可。

133.在 Vue3 中使用 OpenLayers 实现画多边形、任意编辑、遮罩与剪切处理功能

&#x1f3ac; 效果演示截图&#xff08;先睹为快&#xff09; ✨ 功能概览&#xff1a; ✅ 鼠标画任意形状多边形&#xff1b; ✏️ 点击“修改边界”可拖动顶点&#xff1b; &#x1f7e5; 点击“遮罩”后地图除多边形区域外变红&#xff1b; ✂️ 点击“剪切”后仅显示选…

4.8.4 利用Spark SQL实现分组排行榜

在本次实战中&#xff0c;我们的目标是利用Spark SQL实现分组排行榜&#xff0c;特别是计算每个学生分数最高的前3个成绩。任务的原始数据由一组学生成绩组成&#xff0c;每个学生可能有多个成绩记录。我们首先将这些数据读入Spark DataFrame&#xff0c;然后按学生姓名分组&am…