建造者设计模式
3. 建造者设计模式
3.1 原理
Builder 模式,中文翻译为建造者模式
或者构建者模式
,也有人叫它生成器模式
。
建造者模式是用来创建一种类型的复杂对象,通过设置不同的可选参数,“定制化”地创建不同的对象。
创建者模式主要包含以下四个角色:
- 产品(Product):表示将要被构建的复杂对象。
- 抽象创建者(Abstract Builder):定义构建产品的接口,通常包含创建和获取
产品的方法。 - 具体创建者(Concrete Builder):实现抽象创建者定义的接口,为产品的各个
部分提供具体实现。 - 指挥者(Director):负责调用具体创建者来构建产品的各个部分,控制构建过
程。
我们以文档编辑器
为例,假设我们需要创建一个复杂的HTML文档,它包含了标题、段落和图像等元素。我们可以使用创建者设计模式来构建HTML文档。
产品(Product)类 - HTML文档(HtmlDocument):
/*** 类描述:产品(Product),表示将要被构建的复杂对象。* 需求:创建一个复杂的HTML文档,它包含了标题、段落和图像等元素。我们可以使用创建者设计模式来构建HTML文档。** @Author crysw* @Version 1.0* @Date 2023/11/21 22:57*/
public class HtmlDocument {private String header;private String body;private String footer;public void addHeader(String header) {this.header = header;}public void addBody(String body) {this.body = body;}public void addFooter(String footer) {this.footer = footer;}@Overridepublic String toString() {return "<html><head>" + header + "</head><body>" + body + "</body><footer>"+ footer + "</footer></html>";}
}
抽象创建者(Abstract Builder)类 - HtmlDocumentBuilder:
/*** 类描述:抽象创建者(Abstract Builder),定义构建产品的接口,通常包含创建和获取产品的方法。** @Author crysw* @Version 1.0* @Date 2023/11/21 23:01*/
public abstract class HtmlDocumentBuilder {/*** 文档对象*/protected HtmlDocument document;/*** 获取document文档对象** @return*/public HtmlDocument getDocument() {return document;}/*** 创建document文档对象*/public void createNewHtmlDocument() {document = new HtmlDocument();}/*** 构建文档对象的header*/public abstract void buildHeader();/*** 构建文档对象的body*/public abstract void buildBody();/*** 构建文档对象的footer*/public abstract void buildFooter();
}
具体创建者(Concrete Builder)类 - ArticleHtmlDocumentBuilder:
/*** 类描述:具体创建者(Concrete Builder),实现抽象创建者定义的接口,为产品的各个部分提供具体实现。** @Author crysw* @Version 1.0* @Date 2023/11/21 23:04*/
public class ArticleHtmlDocumentBuilder extends HtmlDocumentBuilder {@Overridepublic void buildHeader() {document.addHeader("Article Header");}@Overridepublic void buildBody() {document.addBody("Article Body");}@Overridepublic void buildFooter() {document.addFooter("Article Footer");}
}
指挥者(Director)类 - HtmlDirector:
public class HtmlDirector {/*** 构建者*/private HtmlDocumentBuilder builder;public HtmlDirector(HtmlDocumentBuilder builder) {this.builder = builder;}/*** 构建文档对象*/public void constructDocument() {builder.createNewHtmlDocument();builder.buildHeader();builder.buildBody();builder.buildFooter();}/*** 获取document对象** @return*/public HtmlDocument getDocument() {return builder.getDocument();}
}
现在测试使用创建者设计模式来构建一个HTML文档对象:
/*** 类描述:构建者设计模式的测试** @Author crysw* @Version 1.0* @Date 2023/11/21 23:16*/
@Slf4j
public class BuilderPatternTest {@Testpublic void testHtmlDocumentBuilder() {HtmlDocumentBuilder htmlDocumentBuilder = new ArticleHtmlDocumentBuilder();HtmlDirector director = new HtmlDirector(htmlDocumentBuilder);director.constructDocument();HtmlDocument htmlDocument = director.getDocument();log.info(">>> Constructed HTML Document: \n {}", htmlDocument.toString());}
}
在这个例子中,我们创建了一个表示HTML文档的产品类(HtmlDocument),一个抽象的创建者类(HtmlDocumentBuilder),一个具体的创建者类(ArticleHtmlDocumentBuilder)和一个指挥者类(HtmlDirector)。当我们需要创建一个新的HTML文档对象时,我们可以使用指挥者类
来控制构建过程,从而实现了将构建过程与表示过程的分离。
以上是一个创建者设计模式的标准写法,为了创建一个对象,我们创建了很多辅助的类,总觉得不太合适。在工作中往往不会写的这么复杂,我们可以使用内部类来简化代码,以下是修改后的代码(甚至我们还移除了抽象层):
/*** 类描述:产品(Product),表示将要被构建的复杂对象。* 需求:创建一个复杂的HTML文档,它包含了标题、段落和图像等元素。我们可以使用创建者设计模式来构建HTML文档。** @Author crysw* @Version 1.0* @Date 2023/11/21 22:57*/
public class HtmlDocument {private String header;private String body;private String footer;public void addHeader(String header) {this.header = header;}public void addBody(String body) {this.body = body;}public void addFooter(String footer) {this.footer = footer;}@Overridepublic String toString() {return "<html><head>" + header + "</head><body>" + body + "</body><footer>"+ footer + "</footer></html>";}public static Builder builder() {return new Builder();}public static class Builder {protected HtmlDocument document;private Builder() {document = new HtmlDocument();}public Builder addHeader(String header) {document.addHeader(header);return this;}public Builder addBody(String body) {document.addBody(body);return this;}public Builder addFooter(String footer) {document.addFooter(footer);return this;}public HtmlDocument build() {return document;}}
}
再来测试创建一个HTML文档对象:
/*** 类描述:构建者设计模式的测试** @Author crysw* @Version 1.0* @Date 2023/11/21 23:16*/
@Slf4j
public class BuilderPatternTest {@Testpublic void testHtmlDocumentBuilder2() {HtmlDocument htmlDocument = HtmlDocument.builder().addHeader("This is the header").addBody("This is body").addFooter("This is footer").build();log.info(">>> Constructed HTML Document: \n {}", htmlDocument.toString());}
}
3.2 为什么需要建造者模式
(1) 根据复杂的配置项进行定制化构建。
先看一个mybaits中经典的案例,这个案例中使用了装饰器和创建者设计模式:
public class CacheBuilder {private final String id;private Class<? extends Cache> implementation;private final List<Class<? extends Cache>> decorators;private Integer size;private Long clearInterval;private boolean readWrite;private Properties properties;private boolean blocking;public CacheBuilder(String id) {this.id = id;this.decorators = new ArrayList<Class<? extends Cache>>();}public CacheBuilder implementation(Class<? extends Cache> implementation) {this.implementation = implementation;return this;}public CacheBuilder addDecorator(Class<? extends Cache> decorator) {if (decorator != null) {this.decorators.add(decorator);}return this;}public CacheBuilder size(Integer size) {this.size = size;return this;}public CacheBuilder clearInterval(Long clearInterval) {this.clearInterval = clearInterval;return this;}public CacheBuilder readWrite(boolean readWrite) {this.readWrite = readWrite;return this;}public CacheBuilder blocking(boolean blocking) {this.blocking = blocking;return this;}public CacheBuilder properties(Properties properties) {this.properties = properties;return this;}public Cache build() {setDefaultImplementations();Cache cache = newBaseCacheInstance(implementation, id);setCacheProperties(cache);// issue #352, do not apply decorators to custom caches// 根据配置的装饰器对原有缓存进行增强,如增加淘汰策略等if (PerpetualCache.class.equals(cache.getClass())) {for (Class<? extends Cache> decorator : decorators) {cache = newCacheDecoratorInstance(decorator, cache);setCacheProperties(cache);}cache = setStandardDecorators(cache);} else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {cache = new LoggingCache(cache);}return cache;}
}
我们总结这个案例中的几个特点:
- 参数有必填项id,有很多可选填的内容,通常必选项id通过构造器传入,可选项通过方法传递。
- 真正的构建过程需要调用build方法,构建时需要根据已配置的成员变量的内容选择合适的装饰器,对目标cache进行增强。
(2) 实现不可变对象
创建者设计模式(Builder Design Pattern)可以实现不可变对象,即一旦创建完成,对象的状态就不能改变。这有助于保证对象的线程安全和数据完整性。下面是一个使用创建者设计模式实现的不可变对象的Java示例:
/*** 类描述:实现不可变对象** @Author crysw* @Version 1.0* @Date 2023/11/26 21:21*/
public final class ImmutablePerson {private final String name;private final int age;private final String address;private ImmutablePerson(Builder builder) {this.name = builder.name;this.age = builder.age;this.address = builder.address;}public String getName() {return name;}public int getAge() {return age;}public String getAddress() {return address;}public Builder builder() {return new Builder();}public static class Builder {private String name;private int age;private String address;public Builder() {}public Builder setName(String name) {this.name = name;return this;}public Builder setAge(int age) {this.age = age;return this;}public Builder setAddress(String address) {this.address = address;return this;}public ImmutablePerson build() {return new ImmutablePerson(this);}}
}
在这个例子中, ImmutablePerson 类具有三个属性: name 、age 和 address 。这些属性都是 final 修饰,一旦设置就不能更改。ImmutablePerson 的构造函数是私有的且没有set方法,外部无法直接创建该类的实例。需要使用内部的 Builder 类要创建一个 ImmutablePerson 实例,通过连续调用 Builder 类的方法,来为ImmutablePerson 设置属性。最后调用 build() 方法,创建一个具有指定属性的不可变 ImmutablePerson 实例。
3.3 源码应用
创建者设计模式在源码中有广泛的使用:
(1)jdk中的StringBuilder和StringBuffer,他们的实现不是完全按照标准的创建者设计模式设计,但也是一样的思想。
这两个类用于构建和修改字符串。它们实现了创建者模式,允许通过方法链来修改字符串。这些类在性能上优于 String 类,因为它们允许在同一个对象上执行多次修改,而不需要每次修改都创建一个新的对象。
StringBuilder builder = new StringBuilder();
builder.append("Hello").append(" ").append("World!");
String result = builder.toString();
(2)在ssm源码中使用创建者设计模式,如Spring中的BeanDefinitionBuilder 类,mybatis中SqlSessionFactoryBuilder 、
XMLConfigBuilder 、XMLMapperBuilder 、XMLStatementBuilder 、CacheBuilder 等。
(3)使用lombok实现创建者设计模式
Lombok 是一个 Java 库,它可以简化代码,提高开发效率,尤其是在实现模式和生成常用方法(例如 getter、setter、equals、hashCode 和 toString)时。以下是一个使用 Lombok 的创建者设计模式的例子:
首先在项目中引入了 Lombok 依赖:
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version><scope>provided</scope>
</dependency>
创建一个类, 使用lombok的相关注解实现建造者模式:
/*** 类描述:使用Lombok构建实例,查看编译后的class文件,会自动生成构建对象的代码(使用的构建者模式)** @Author crysw* @Version 1.0* @Date 2023/11/20 23:19*/
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class Person {private String name;private Integer age;
}
反编译后的Person类:
package cn.itcast.designPatterns.builder;public class Person {private String name;private Integer age;public static PersonBuilder builder() {return new PersonBuilder();}public Person() {}public Person(String name, Integer age) {this.name = name;this.age = age;}public String getName() {return this.name;}public Integer getAge() {return this.age;}public void setName(String name) {this.name = name;}public void setAge(Integer age) {this.age = age;}public boolean equals(Object o) {if (o == this) {return true;} else if (!(o instanceof Person)) {return false;} else {Person other = (Person)o;if (!other.canEqual(this)) {return false;} else {Object this$name = this.getName();Object other$name = other.getName();if (this$name == null) {if (other$name != null) {return false;}} else if (!this$name.equals(other$name)) {return false;}Object this$age = this.getAge();Object other$age = other.getAge();if (this$age == null) {if (other$age != null) {return false;}} else if (!this$age.equals(other$age)) {return false;}return true;}}}protected boolean canEqual(Object other) {return other instanceof Person;}public int hashCode() {int PRIME = true;int result = 1;Object $name = this.getName();result = result * 59 + ($name == null ? 43 : $name.hashCode());Object $age = this.getAge();result = result * 59 + ($age == null ? 43 : $age.hashCode());return result;}public String toString() {return "Person(name=" + this.getName() + ", age=" + this.getAge() + ")";}// 内部类建造者public static class PersonBuilder {private String name;private Integer age;PersonBuilder() {}public PersonBuilder name(String name) {this.name = name;return this;}public PersonBuilder age(Integer age) {this.age = age;return this;}public Person build() {return new Person(this.name, this.age);}public String toString() {return "Person.PersonBuilder(name=" + this.name + ", age=" + this.age + ")";}}
}
测试lombok创建的建造者模式实现的类
@Test
public void testPerson() {Person person = builder().name("crysw").age(23).build();person.setName("panda");person.setAge(22);log.info(">>> person: {}", person);}
相关文章:
建造者设计模式
3. 建造者设计模式 3.1 原理 Builder 模式,中文翻译为建造者模式或者构建者模式,也有人叫它生成器模式。 建造者模式是用来创建一种类型的复杂对象,通过设置不同的可选参数,“定制化”地创建不同的对象。 创建者模式主要包含以…...

YOLO目标检测——垃圾检测数据集下载分享【含对应voc、coco和yolo三种格式标签】
实际项目应用:智能化垃圾分类系统、垃圾回收和处理领域的优化管理等方面数据集说明:垃圾分类检测数据集,真实场景的高质量图片数据,数据场景丰富,含报纸、蛋壳、矿泉水瓶、电池、拉链顶罐、塑料餐盒、纸质药盒、香蕉皮…...
Vue CLI的介绍【vue利器之一】
文章目录 前言Vue CLI 介绍CLICLI 服务CLI 插件后言 前言 hello world欢迎来到前端的新世界 😜当前文章系列专栏:vue.js 🐱👓博主在前端领域还有很多知识和技术需要掌握,正在不断努力填补技术短板。(如果出现错误&am…...
【学习笔记】插值之拉格朗日插值(Lagrange)
0 插值介绍 插值法是广泛应用于理论研究和工程实际的重要数值方法。用提供的部分离散的函数值来进行理论分析和设计都是极不方便的,因此希望能够用一个既能反映原函数特征,又便于计算的简单函数去近似原函数。 1 低次拉格朗日插值 定理:设…...

无人机电力巡检系统运行流程全解读
随着电力行业体系不断完善,保障电网运营的安全成为至关重要的任务。传统的人工巡检方式在面对电力设备广泛分布和复杂工况时显得效率低下,为了解决这一难题,无人机电力巡检系统应运而生,以智能化的运行流程,为电网安全…...
有关全局变量和sizeof的题
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int i; int main() {i--;if (i > sizeof(i)){printf(">");}else{printf("<");}return 0; } 这道题结果是 > 首先对于一个全局变量,当没有对其初始化时,它…...
vue简述
vue为渐进式框架:vmmv 1.易用 有html、css、javascript基础,即可学习vue框架 2.高效、开发前端页面 非常高效 1.vue的体积小、压缩完只需要20k的大小 2.超快的虚拟dom操作js中非常多的dom操作,vue设计虚拟dom非常快 3.设计时vue底层深度优化 …...

YOLOv8 训练自己的分割数据集
之前写过一篇 使用YOLOv8训练自己的【目标检测】数据集-【收集数据集】-【标注数据集】-【划分数据集】-【配置训练环境】-【训练模型】-【评估模型】-【导出模型】,里面带大家整个流程走过一遍了, 这篇文章我们来介绍如何使用 YOLOv8 训练分割数据集&a…...

Python实现DDos攻击实例详解
文章目录 SYN 泛洪攻击Scapy3k 基本用法代码实现DDos 实现思路argparse 模块socket 模块代码实现Client 端程序测试后记关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战案…...

微信小程序实现【点击 滑动 评分 评星(5星)】功能
wxml文件: <view class"wxpl_xing"><view class"manyidu">{{scoreContent}}</view><view><block wx:for{{scoreArray}} wx:for-item"item"><view classstarLen bindtapchangeScore data-sy"{{…...
堡垒机的用途
堡垒机的用途 堡垒机,即在一个特定的网络环境下,为了保障网络和数据不受来自外部和内部用户的入侵和破坏,而运用各种技术手段监控和记录运维人员对网络内的服务器、网络设备、安全设备、数据库等设备的操作行为,以便集中报警、及时…...

超全超实用行业解决方案合集,覆盖十大行业数据应用需求
现代企业面对复杂的业务需求,对数据分析的需求日益增加。 从实时销售到市场趋势,从客户行为到产品优化,每个环节都依赖于数据支持。然而,传统的数据分析平台常分散在不同系统和团队中,形成数据孤岛,降低了…...

一盏茶的时间,入门 Node.js
一、.什么是 Node.js? Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时,用于构建高性能、可伸缩的网络应用。 它采用事件驱动、非阻塞 I/O 模型,使其在处理并发请求时表现出色。 二、安装 Node.js 首先,让我们从 Node.…...
关于Java多线程的一些随笔
Synchronized与ReentrantLock有哪些相同点和不同点? 在Java中,synchronized关键字和ReentrantLock类都用于管理线程间的同步,但它们在实现方式、功能和灵活性方面存在一些差异。以下是它们的相同点和不同点: 相同点 互斥性&…...
Answering difficult questions in other way
I’m not (too) sure Q:Do you think computers make life easier? A:I’m not (too) sure, to be honest, but I reckon they do make life easier because… I can’t say for sure, but … Q:Do you think computers make lif…...
RabbitMQ教程:Linux下安装、基本命令与Spring Boot集成
RabbitMQ教程:Linux下安装、基本命令与Spring Boot集成 1. RabbitMQ简介 RabbitMQ是一个开源的消息代理和队列服务器,用于通过轻量级消息传递协议(AMQP)在分布式系统中传递消息。它支持多种编程语言,包括Java、Pytho…...

王者荣耀小游戏
第一步是创建项目 项目名自拟 第二部创建个包名 来规范class 然后是创建类 GameFrame 运行类 package com.sxt; package com.sxt;import java.awt.Graphics; import java.awt.Image; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.…...

JAVA小游戏“简易版王者荣耀”
第一步是创建项目 项目名自拟 第二部创建个包名 来规范class 然后是创建类 GameFrame 运行类 package com.sxt;import java.awt.Graphics; import java.awt.Image; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; im…...

Nginx高级
Nginx高级 第一部分:扩容 通过扩容提升整体吞吐量 1.单机垂直扩容:硬件资源增加 云服务资源增加 整机:IBM、浪潮、DELL、HP等 CPU/主板:更新到主流 网卡:10G/40G网卡 磁盘:SAS(SCSI) HDD(机械…...

深度学习中小知识点系列(三) 解读Mosaic 数据增强
前言 Mosaic数据增强,这种数据增强方式简单来说就是把4张图片,通过随机缩放、随机裁减、随机排布的方式进行拼接。Mosaic有如下优点: (1)丰富数据集:随机使用4张图片,随机缩放,再随…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...

IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...