创建型模式:建造者模式

什么是建造者模式

建造者模式(Builder Pattern)是一种创建型设计模式,它将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。简单来说,建造者模式允许您一步一步创建复杂对象,而不是一次性传入所有参数。

建造者模式特别适合那些需要多个步骤构建、有多个可选参数或者构建过程中需要遵循特定顺序的对象创建场景。

为什么需要建造者模式

假设我们有一个具有十多个属性的Computer类,其中一些属性是必需的,而其他属性是可选的。使用传统的构造函数或JavaBean模式创建对象会带来以下问题:

  1. 伸缩构造函数问题:需要编写多个构造函数来处理不同的参数组合
  2. 可读性差:当参数较多时,构造函数调用难以理解
  3. 状态不一致:使用setter方法时,对象可能处于不完整状态
  4. 线程安全问题:构建过程中对象状态可能发生变化

建造者模式优雅地解决了这些问题。

建造者模式的核心实现

// 产品类
public class House {// 必要参数private final String foundation;private final String structure;private final String roof;// 可选参数private final String interior;private final String exterior;private final boolean hasGarage;private final boolean hasSwimmingPool;private final boolean hasGarden;// 私有构造函数,只能通过Builder访问private House(Builder builder) {this.foundation = builder.foundation;this.structure = builder.structure;this.roof = builder.roof;this.interior = builder.interior;this.exterior = builder.exterior;this.hasGarage = builder.hasGarage;this.hasSwimmingPool = builder.hasSwimmingPool;this.hasGarden = builder.hasGarden;}// Getterspublic String getFoundation() { return foundation; }public String getStructure() { return structure; }public String getRoof() { return roof; }public String getInterior() { return interior; }public String getExterior() { return exterior; }public boolean hasGarage() { return hasGarage; }public boolean hasSwimmingPool() { return hasSwimmingPool; }public boolean hasGarden() { return hasGarden; }@Overridepublic String toString() {return "House with " + foundation + " foundation, " + structure + " structure, " + roof + " roof, " + interior + " interior, " +exterior + " exterior. " +(hasGarage ? "Has garage. " : "") +(hasSwimmingPool ? "Has swimming pool. " : "") +(hasGarden ? "Has garden." : "");}// 静态内部Builder类public static class Builder {// 必要参数private final String foundation;private final String structure;private final String roof;// 可选参数 - 设置默认值private String interior = "普通装修";private String exterior = "标准外墙";private boolean hasGarage = false;private boolean hasSwimmingPool = false;private boolean hasGarden = false;// 必要参数通过构造器强制传入public Builder(String foundation, String structure, String roof) {this.foundation = foundation;this.structure = structure;this.roof = roof;}// 可选参数通过具有链式调用的setter方法设置public Builder interior(String interior) {this.interior = interior;return this;}public Builder exterior(String exterior) {this.exterior = exterior;return this;}public Builder garage(boolean hasGarage) {this.hasGarage = hasGarage;return this;}public Builder swimmingPool(boolean hasSwimmingPool) {this.hasSwimmingPool = hasSwimmingPool;return this;}public Builder garden(boolean hasGarden) {this.hasGarden = hasGarden;return this;}// build方法创建最终对象public House build() {// 可以在这里添加构建验证逻辑return new House(this);}}
}

建造者模式的关键点

  1. 产品类:复杂对象,通常是不可变的
  2. Builder类:负责定义产品创建步骤的接口
  3. 链式调用:Builder方法返回Builder自身,支持流式API
  4. 最终构建方法:完成产品创建的方法(如build()
  5. 参数分离:将必选参数和可选参数明确区分

使用建造者模式

public class BuilderPatternDemo {public static void main(String[] args) {// 使用Builder创建对象House simpleHouse = new House.Builder("混凝土基础", "砖墙结构", "平顶屋顶").build();System.out.println("简易房屋:");System.out.println(simpleHouse);// 使用Builder创建更复杂的对象,设置可选参数House luxuryHouse = new House.Builder("深层钢筋混凝土基础", "钢筋混凝土框架", "坡顶屋顶").interior("豪华装修").exterior("大理石外墙").garage(true).swimmingPool(true).garden(true).build();System.out.println("\n豪华房屋:");System.out.println(luxuryHouse);}
}

运行结果

简易房屋:
House with 混凝土基础 foundation, 砖墙结构 structure, 平顶屋顶 roof, 普通装修 interior, 标准外墙 exterior. 豪华房屋:
House with 深层钢筋混凝土基础 foundation, 钢筋混凝土框架 structure, 坡顶屋顶 roof, 豪华装修 interior, 大理石外墙 exterior. Has garage. Has swimming pool. Has garden.

建造者模式的高级版本:带Director的建造者模式

在某些情况下,我们可能需要一个额外的Director类来封装构建过程,特别是当构建过程复杂或需要被重用时。

// 抽象Builder接口
public interface HouseBuilder {void buildFoundation();void buildStructure();void buildRoof();void buildInterior();void buildExterior();void buildGarage();void buildSwimmingPool();void buildGarden();House getResult();
}// 具体Builder实现
public class StandardHouseBuilder implements HouseBuilder {private String foundation;private String structure;private String roof;private String interior;private String exterior;private boolean hasGarage;private boolean hasSwimmingPool;private boolean hasGarden;@Overridepublic void buildFoundation() {this.foundation = "标准混凝土基础";}@Overridepublic void buildStructure() {this.structure = "砖混结构";}@Overridepublic void buildRoof() {this.roof = "普通瓦片屋顶";}@Overridepublic void buildInterior() {this.interior = "基础装修";}@Overridepublic void buildExterior() {this.exterior = "普通外墙";}@Overridepublic void buildGarage() {this.hasGarage = false;}@Overridepublic void buildSwimmingPool() {this.hasSwimmingPool = false;}@Overridepublic void buildGarden() {this.hasGarden = false;}@Overridepublic House getResult() {return new House(foundation, structure, roof, interior, exterior, hasGarage, hasSwimmingPool, hasGarden);}
}// 另一个具体Builder实现
public class LuxuryHouseBuilder implements HouseBuilder {private String foundation;private String structure;private String roof;private String interior;private String exterior;private boolean hasGarage;private boolean hasSwimmingPool;private boolean hasGarden;@Overridepublic void buildFoundation() {this.foundation = "深层钢筋混凝土基础";}// 其他方法实现...@Overridepublic House getResult() {return new House(foundation, structure, roof, interior, exterior, hasGarage, hasSwimmingPool, hasGarden);}
}// Director类
public class HouseDirector {private HouseBuilder builder;public HouseDirector(HouseBuilder builder) {this.builder = builder;}// 更换Builderpublic void changeBuilder(HouseBuilder builder) {this.builder = builder;}// 构建最小可居住房屋public void constructMinimalHouse() {builder.buildFoundation();builder.buildStructure();builder.buildRoof();builder.buildInterior();builder.buildExterior();}// 构建完整功能房屋public void constructFullFeaturedHouse() {builder.buildFoundation();builder.buildStructure();builder.buildRoof();builder.buildInterior();builder.buildExterior();builder.buildGarage();builder.buildSwimmingPool();builder.buildGarden();}
}

实际应用示例:流畅的API构建器

下面通过一个SQL查询构建器的例子来展示建造者模式在实际API设计中的应用:

// SQL查询构建器
public class SQLQueryBuilder {private String table;private List<String> columns = new ArrayList<>();private List<String> conditions = new ArrayList<>();private List<String> orderBy = new ArrayList<>();private Integer limit;private Integer offset;public SQLQueryBuilder from(String table) {this.table = table;return this;}public SQLQueryBuilder select(String... columns) {if (columns.length == 0) {this.columns.add("*");} else {this.columns.addAll(Arrays.asList(columns));}return this;}public SQLQueryBuilder where(String condition) {this.conditions.add(condition);return this;}public SQLQueryBuilder orderBy(String column, boolean ascending) {this.orderBy.add(column + (ascending ? " ASC" : " DESC"));return this;}public SQLQueryBuilder limit(int limit) {this.limit = limit;return this;}public SQLQueryBuilder offset(int offset) {this.offset = offset;return this;}public String build() {// 验证必要参数if (table == null) {throw new IllegalStateException("表名未指定");}StringBuilder query = new StringBuilder();query.append("SELECT ");// 添加列if (columns.isEmpty()) {query.append("*");} else {query.append(String.join(", ", columns));}// 添加表query.append(" FROM ").append(table);// 添加条件if (!conditions.isEmpty()) {query.append(" WHERE ");query.append(String.join(" AND ", conditions));}// 添加排序if (!orderBy.isEmpty()) {query.append(" ORDER BY ");query.append(String.join(", ", orderBy));}// 添加限制if (limit != null) {query.append(" LIMIT ").append(limit);}// 添加偏移if (offset != null) {query.append(" OFFSET ").append(offset);}return query.toString();}
}

SQL构建器的使用示例

public class SQLBuilderDemo {public static void main(String[] args) {// 创建简单查询String simpleQuery = new SQLQueryBuilder().from("users").select("id", "name", "email").build();System.out.println("简单查询:");System.out.println(simpleQuery);// 创建复杂查询String complexQuery = new SQLQueryBuilder().select("u.id", "u.name", "COUNT(o.id) as order_count").from("users u LEFT JOIN orders o ON u.id = o.user_id").where("u.status = 'active'").where("o.create_time > '2023-01-01'").orderBy("order_count", false).limit(10).offset(20).build();System.out.println("\n复杂查询:");System.out.println(complexQuery);}
}

建造者模式的常见应用场景

  1. 文档生成器:构建HTML、PDF、Word文档
  2. 复杂对象构建:具有多个配置选项的对象
  3. 不可变对象创建:需要一次性设置所有属性的对象
  4. 流畅的API设计:提供链式调用的接口
  5. 配置构建:应用程序配置、网络请求配置等
  6. 测试数据构建:创建测试用例数据
  7. 复杂GUI创建:组装UI组件和布局

真实世界中的建造者模式应用

  1. Java中的StringBuilder/StringBuffer:字符串构建
  2. Lombok的@Builder注解:自动生成Builder代码
  3. Spring框架的UriComponentsBuilder:构建URI
  4. Apache Camel的RouteBuilder:构建路由规则
  5. Retrofit的RequestBuilder:构建HTTP请求
  6. OkHttp的Request.Builder:构建HTTP请求

建造者模式的优点

  1. 参数控制:分离必选参数和可选参数
  2. 构建过程封装:隐藏复杂构建过程的细节
  3. 可读性:创建对象的代码更具可读性
  4. 灵活性:同一构建过程可创建不同表示
  5. 不变性:可以创建不可变对象
  6. 参数验证:可在构建时检查参数有效性

建造者模式的缺点

  1. 代码量增加:需要创建额外的Builder类
  2. 复杂度:对于简单对象,可能过于复杂
  3. 性能开销:相比直接构造,略有性能损失
  4. 维护成本:当产品类变化时,需要同步修改Builder

建造者模式与工厂模式的区别

虽然建造者模式和工厂模式都是创建型模式,但它们有明显区别:

  • 工厂模式关注对象创建种类(what)
  • 建造者模式关注复杂对象构建过程(how)

工厂用于创建整个对象,而建造者关注对象各个部分的创建过程和装配顺序。

建造者模式变体:自引用泛型流畅接口

这种高级变体使用泛型保持链式调用时的类型安全:

public class Email {// 邮件属性private final String from;private final List<String> to;private final String subject;private final String body;private final List<String> attachments;private Email(Builder builder) {this.from = builder.from;this.to = new ArrayList<>(builder.to);this.subject = builder.subject;this.body = builder.body;this.attachments = new ArrayList<>(builder.attachments);}// 泛型Builder基类public static class Builder<T extends Builder<T>> {private String from;private List<String> to = new ArrayList<>();private String subject = "";private String body = "";private List<String> attachments = new ArrayList<>();// 自引用泛型类型转换@SuppressWarnings("unchecked")protected final T self() {return (T) this;}public T from(String from) {this.from = from;return self();}public T to(String address) {this.to.add(address);return self();}public T subject(String subject) {this.subject = subject;return self();}public T body(String body) {this.body = body;return self();}public T attachment(String attachment) {this.attachments.add(attachment);return self();}public Email build() {validateRequiredFields();return new Email(this);}private void validateRequiredFields() {if (from == null || from.isEmpty()) {throw new IllegalStateException("发件人不能为空");}if (to.isEmpty()) {throw new IllegalStateException("收件人不能为空");}}}// 具体Builderpublic static class EmailBuilder extends Builder<EmailBuilder> {// 不需要额外代码,因为基类已实现了所有功能}// 便捷的静态工厂方法public static EmailBuilder builder() {return new EmailBuilder();}@Overridepublic String toString() {return "Email{" +"from='" + from + '\'' +", to=" + to +", subject='" + subject + '\'' +", body='" + body + '\'' +", attachments=" + attachments +'}';}
}

建造者模式最佳实践

  1. 使用场景选择:对象参数较多或构建过程复杂时使用
  2. 参数验证:在build()方法中进行完整性校验
  3. 必选与可选分离:通过构造函数传入必选参数
  4. 不可变对象:建造者模式是创建不可变对象的好方法
  5. 链式API设计:返回this支持流畅接口
  6. 避免过早优化:简单对象不需要建造者模式
  7. 使用嵌套静态类:使Builder成为产品的静态内部类

建造者模式小结

建造者模式是一种强大的创建型设计模式,它通过分离复杂对象的构建过程与表示,使得创建过程更加灵活和可控。它特别适合于有多个参数选项的复杂对象创建,可以提高代码的可读性和可维护性。

在实际应用中,许多现代API和框架都采用了建造者模式的变体来提供流畅的接口。掌握这一模式有助于我们设计出更易用、更灵活的API,并解决对象创建中的复杂性问题。

无论是创建不可变对象,还是设计流畅的API,建造者模式都是一种值得掌握的重要设计模式。

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

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

相关文章

Linux `init 5` 相关命令的完整使用指南

Linux init 5 相关命令的完整使用指南—目录 一、init 系统简介二、init 5 的含义与作用三、不同 Init 系统下的 init 5 行为1. SysVinit&#xff08;如 CentOS 6、Debian 7&#xff09;2. systemd&#xff08;如 CentOS 7、Ubuntu 16.04&#xff09;3. Upstart&#xff08;如 …

RabbitMQ常见面试题回答重点

文章目录 什么是消息队列&#xff1f;为什么需要消息队列消息队列的模型消息队列常见名词如何保证消息不丢失&#xff1f;&#xff08;可靠性&#xff09;如何保证消息不重复/业务幂等性如何保证消息有序性如何处理消息堆积消息队列设计为推送还是拉取 / 推拉模式优点无法路由的…

欣佰特携数十款机器人相关前沿产品,亮相第二届人形机器人和具身智能行业盛会

2025年4月15日至16日&#xff0c;备受关注的第二届中国人形机器人与具身智能产业大会已在北京成功举行。作为国内前沿科技及产品服务领域的重要参与者&#xff0c;欣佰特科技携众多前沿产品精彩亮相&#xff0c;全方位展示了其在人形机器人与具身智能领域的创新产品。 在本次大…

Docker安装 (centos)

1.安装依赖包&#xff1a; sudo yum install -y yum-utils device-mapper-persistent-data lvm2 2.删除已有的 Docker 仓库文件&#xff08;如果有&#xff09;&#xff1a; sudo rm -f /etc/yum.repos.d/docker-ce.repo 3.添加阿里云的 Docker 仓库&#xff1a; sudo yum…

Vue接口平台学习十——接口用例页面2

效果图及简单说明 左边选择用例&#xff0c;右侧就显示该用例的详细信息。 使用el-collapse折叠组件&#xff0c;将请求到的用例详情数据展示到页面中。 所有数据内容&#xff0c;绑定到caseData中 // 页面绑定的用例编辑数据 const caseData reactive({title: "",…

服务器数据迁移指南

服务器数据迁移是将数据从一台服务器转移到另一台服务器的过程&#xff0c;可能是为了硬件升级、云迁移、数据中心搬迁或服务整合。 以下是数据迁移的关键步骤和注意事项&#xff1a; 迁移前准备 制定迁移计划 确定迁移范围(全量/增量) 评估数据量和网络带宽 制定时间表和回…

25.解决中医知识问答删除历史对话功能后端处理请求时抛出异常

ChatTest.vue:176 DELETE http://localhost:8080/api/chat/conversations/20 500 (Internal Server Error) deleteConversation ChatTest.vue:176 onClick ChatTest.vue:22 ChatTest.vue:185 删除失败 AxiosError {message: Request failed with status code 500, name: Axio…

记录seatunnel排查重复数据的案例分析

文章目录 背景分析检查现象检查B集群是否有异常&#xff0c;导致重复消费的分析同步任务 修复问题发现flink job 一直报异常修复问题 背景 使用seatunnel 同步数据从A 集群kafka 同步到B集群kafka,现象是发现两边数据不一致&#xff0c;每天10w级别会多几十条数据 分析 检查…

VSCode远程图形化GDB

VSCode远程图形化GDB 摘要一、安装VSCode1、使用.exe安装包安装VSCode2、VSCode 插件安装3、VSCode建立远程连接 二、core dump找bug1、开启core文件2、永久生效的方法3、编写测试程序4、运行结果5、查看core段错误位置6、在程序中开启core dump并二者core文件大小 三、gdbserv…

Android 中实现 GIF 图片动画

在 Android 中&#xff0c;ImageView 从 Android 9.0&#xff08;API 级别 28&#xff09; 开始原生支持 GIF 动画&#xff0c;通过 AnimatedImageDrawable 类实现。在之前的版本中&#xff0c;ImageView 并不支持直接播放 GIF 动画&#xff0c;只能显示 GIF 的第一帧。 一、 …

【c语言】指针进阶

目录 1.字符指针 2.指针数组 3.数组指针 3.1 数组指针的定义 3.2 数组指针的使用 4.数组参数&#xff0c;指针参数 4.1 一维数组传参 4.2 二维数组传参 4.3 一级指针传参 4.4 二级指针传参 5.函数指针 6.函数指针数组 6.1函数指针数组的定义 6.2 函数指针数组…

极狐GitLab 项目 API 的速率限制如何设置?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;关于中文参考文档和资料有&#xff1a; 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 项目 API 的速率限制 (BASIC SELF) 引入于 15.10 版本&#xff0c;功能标志为rate_limit_for_unauthenticated_projects_api_…

【xlog日志文件】怎么删除里面包含某些字符串的行(使用excel)

将log日志,复制到单独一行 B列&#xff08;可能一行很长&#xff0c;所以将整合后的放在A列&#xff09; 使用公式可以筛选出 包含某些字符串的行 为true&#xff0c;将这些行直接删除 IF(COUNT(FIND("MediaMuxterThreadRussia",B2,1))>0,"包含",&quo…

STM32提高篇: CAN通讯

STM32提高篇: CAN通讯 一.CAN通讯介绍1.物理层2.协议层二.STM32CAN外设1.CAN控制器的3种工作模式2.CAN控制器的3种测试模式3.功能框图三.CAN的寄存器介绍1.环回静默模式测试2.双击互发测试四.CAN的HAL代码解读一.CAN通讯介绍 CAN(Controller Area Network 控制器局域网,简称…

Java写数据结构:栈

1.概念&#xff1a; 一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last In First Out&#xff09;的原则。 压栈&#xff1a;栈的插…

单页面应用的特点,什么是路由,VueRouter的下载,安装和使用,路由的封装抽离,声明式导航的介绍和使用

文章目录 一.什么是单页面应用?二.什么是路由?生活中的路由和Vue中的路由 三.VueRouter(重点)0.引出1.介绍2.下载与使用(5个基本步骤2个核心步骤)2.1 五个基本步骤2.2 两个核心步骤 四.路由的封装抽离五.声明式导航1.导航链接特点一:能跳转特点二:能高亮 2.两个高亮类名2.1.区…

【C++】模板2.0

最近学习了一些模板的知识&#xff0c;速写本博客作为学习笔记&#xff0c;若有兴趣&#xff0c;欢迎垂阅读&#xff01; 1.非类型模板参数 模板参数分类类型形参与非类型形参。 类型形参即&#xff1a;出现在模板参数列表中&#xff0c;跟在class或者typename之类的参数类型名…

目标检测中的损失函数(二) | BIoU RIoU α-IoU

BIoU来自发表在2018年CVPR上的文章&#xff1a;《Improving Object Localization With Fitness NMS and Bounded IoU Loss》 论文针对现有目标检测方法只关注“足够好”的定位&#xff0c;而非“最优”的框&#xff0c;提出了一种考虑定位质量的NMS策略和BIoU loss。 这里不赘…

如何在 Amazon EC2 上部署 Java(Spring Boot 版)

让我们学习如何将 Java Spring Boot Web 服务器部署到 Amazon EC2。每月只需 3 美元。 使用 Azure&#xff0c;您可能不知道要花费多少钱。 Spring Boot 项目示例 在本教程中&#xff0c;我们将重点介绍如何将 Java Spring Boot 服务器部署到 Amazon EC2&#xff0c;因此我们不…

Git常用命令分类汇总

Git常用命令分类汇总 一、基础操作 初始化仓库git init添加文件到暂存区git add file_name # 添加单个文件 git add . # 添加所有修改提交更改git commit -m "提交描述"查看仓库状态git status二、分支管理 创建/切换分支git branch branch_name …