设计模式-建造者模式 - 实践

news/2025/10/22 18:10:53/文章来源:https://www.cnblogs.com/wzzkaifa/p/19158660

设计模式-建造者模式

案例分析

思考这样一个场景,现在需要实现一个简单的线程池,但是有一些要求,如果不传队列长度字段,就必须传最大线程数,原因是这样的处理方式为来一个请求就创建一个新线程,如果不限制最大线程数可能会导致资源耗尽系统崩溃。

package com.xsdl.builder;
public class ThreadPool
{
private Integer coreSize;
private Integer maxSize;
private Integer queueSize;
}

如果按照正常的开发,比如暴露所有属性的 set 方法,会有以下的问题:

  • 不传队列长度字段,就必须传最大线程数的校验该放到哪里去做
package com.xsdl.builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class ThreadPool
{
private Integer coreSize;
private Integer maxSize;
private Integer queueSize;
public boolean validate() {
// 传了队列长度
if (queueSize != null && queueSize >
0) {
return true;
}
// 没有传队列长度,那么必须传最大线程数
return maxSize != null;
}
}
  • 用户设置了部分值后就使用这个对象该怎么办,对象在设置完成之前应该是无效且无法使用的
package com.xsdl.builder;
public class Main
{
public static void main(String[] args) {
ThreadPool threadPool = new ThreadPool();
threadPool.setCoreSize(10);
// 用户如果不做校验直接开始使用怎么办
System.out.println(threadPool.validate());
}
}

以上这些问题也不是不能解决,比如定义 n 个有参构造器,限制类只能通过有参构造器来进行创建对象,在构造器里对参数进行校验。但这样会导致拓展性极差,且在条件复杂时很难处理。

于是就引入了建造者模式,建造者模式一般单独定义一个 Builder 类或定义为主类里的内部类,主类不允许通过直接创建,而是先创建 Builder 类,并在设置完成所有属性后调用 build 方法来创建实例,如下所示:

package com.xsdl.builder;
import lombok.Data;
import lombok.experimental.Accessors;
public class ThreadPool
{
private Integer coreSize;
private Integer maxSize;
private Integer queueSize;
// 私有构造方法,防止外部实例化
private ThreadPool(Builder builder) {
this.coreSize = builder.coreSize;
this.maxSize = builder.maxSize;
this.queueSize = builder.queueSize;
}
@Data
@Accessors(chain = true)
public static class Builder
{
private Integer coreSize;
private Integer maxSize;
private Integer queueSize;
public ThreadPool build() {
if (!validate()) {
throw new IllegalArgumentException("没有队列长度必须传入最大线程数");
}
return new ThreadPool(this);
}
private boolean validate() {
// 传了队列长度
if (queueSize != null && queueSize >
0) {
return true;
}
// 没有传队列长度,那么必须传最大线程数
return maxSize != null;
}
}
}

其中需要注意的地方:

  • 主类的构造器必须私有化,防止从外界直接创建
  • 中间的操作永远不会返回主类对象,只有在调用 build 方法后才能获得到真正的主类对象,校验的方法也在其中
package com.xsdl.builder;
public class Main
{
public static void main(String[] args) {
ThreadPool build = new ThreadPool.Builder()
.setCoreSize(10)
.setMaxSize(100)
.build();
}
}
拓展分析

如果你经常使用 Java 语言进行开发,那么一定用过 Stream 流,Stream 流在对于容器的遍历和处理上非常方便,并将操作分为中间操作和终端操作,调用完终端操作后就没办法再调用中间操作了。深究其原因,中间操作返回的依然是 Stream 而终端操作例如 count、collect 后返回的对象已经变了。

那我们能不能利用 stream 流的思想和建造者模式如何实现一个简单的 sql 生成器呢?

image-20250924141706816

以 update操作为例,update table set columnA = ‘A’ and columnB = ‘B’ where …

因为 update 是可以更新无数个字段的,可以使用一个 Map<> 来存储 column 和要更新为的 value,这个方法可以无限的调用,但是当调用 where 后就不能再向 Map<> 里追加值了,原因是 where 语句已经生成,如果后续再跟 columnA = ‘A’ 会变成查询条件而不是更新内容。

image-20250924141013439

所以我们实现的功能其实是,如何限制在调用完一个方法后不能再调用同一个类的另一个方法,多态可以很好的满足我们的需求

package com.xsdl.builder;
public class SqlGenerate
{
private SqlGenerate() {
}
public static Init createUpdateSql() {
return new SqlBuilder();
}
public interface Init {
Update table(String tableName);
}
public interface Update {
Update setString(String column, String value);
Update setRaw(String column, String value);
Where where();
}
public interface Where {
Where eqString(String column, String value);
Where eqRaw(String column, String value);
String build();
}
}

定义上述的三个接口,SqlGenerate 类只有一个私有构造器,无法通过外界 new 创建,对外仅提供一个方法 createUpdateSql ,该方法返回一个 Init 接口对象,而该接口对象只能调用 table 方法返回 Update 接口对象,Update 接口对象可以进行多次的 set 值,也可以调用 where 方法返回 where 接口对象。如前图所示。

实现
package com.xsdl.builder;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;
public final class SqlGenerate
{
private SqlGenerate() {
}
public static Init createUpdateSql() {
return new SqlBuilder();
}
public interface Init {
Update table(String tableName);
}
public interface Update {
Update setString(String column, String value);
Update setRaw(String column, String value);
Where where();
}
public interface Where {
Where eqString(String column, String value);
Where eqRaw(String column, String value);
String build();
}
private static final class SqlBuilder
implements Init, Update, Where {
private String tableName;
private final Map<
String, String> setStringMap = new LinkedHashMap<
>();
private final Map<
String, String> setRawMap = new LinkedHashMap<
>();
private final Map<
String, String> whereStringMap = new LinkedHashMap<
>();
private final Map<
String, String> whereRawMap = new LinkedHashMap<
>();
@Override
public Update table(String tableName) {
this.tableName = tableName;
return this;
}
@Override
public Update setString(String column, String value) {
setStringMap.put(column, value);
return this;
}
@Override
public Update setRaw(String column, String value) {
setRawMap.put(column, value);
return this;
}
@Override
public Where where() {
return this;
}
@Override
public Where eqString(String column, String value) {
whereStringMap.put(column, value);
return this;
}
@Override
public Where eqRaw(String column, String value) {
whereRawMap.put(column, value);
return this;
}
@Override
public String build() {
return "UPDATE " + tableName +
" SET " + concatAssignments(setStringMap, setRawMap) +
" WHERE " + concatConditions(whereStringMap, whereRawMap);
}
private static String concatAssignments(Map<
String, String> str, Map<
String, String> raw) {
return concatMap(str, true, ", ") + (str.isEmpty() || raw.isEmpty() ? "" : ", ")
+ concatMap(raw, false, ", ");
}
private static String concatConditions(Map<
String, String> str, Map<
String, String> raw) {
return concatMap(str, true, " AND ") + (str.isEmpty() || raw.isEmpty() ? "" : " AND ")
+ concatMap(raw, false, " AND ");
}
private static String concatMap(Map<
String, String> map, boolean quoted, String delimiter) {
return map.entrySet()
.stream()
.map(e -> e.getKey() + " = " + (quoted ? "'" + e.getValue() + "'" : e.getValue()))
.collect(Collectors.joining(delimiter));
}
}
}

程序中使用以下四个对象保存 set、eq 方法传入的参数值:

private final Map<
String, String> setStringMap = new LinkedHashMap<
>();
private final Map<
String, String> setRawMap = new LinkedHashMap<
>();
private final Map<
String, String> whereStringMap = new LinkedHashMap<
>();
private final Map<
String, String> whereRawMap = new LinkedHashMap<
>();

测试如下:

package com.xsdl.builder;
public class Main
{
public static void main(String[] args) {
String sql = SqlGenerate.createUpdateSql()
.table("user")
.setString("name", "zhangsan")
.setRaw("age", "3")
.where()
.eqString("1", "1")
.eqRaw("sex", "1")
.build();
System.out.println(sql);
}
}

输出结果:UPDATE user SET name = ‘zhangsan’, age = 3 WHERE 1 = ‘1’ AND sex = 1

与预期的设计相符,用类似 stream 的方法实现生成 update sql 语句的功能,虽然也有一些不足,例如数据库中的字符串类型需要加 ‘’ ,因此程序把字符串参数和其他参数分为两个 map 存储,如果先调用 setString 再 setRaw 再 setString,最后生成的语句会把所有 String 的条件生成在前面,丢失了传入的绝对顺序;同时目前的 where 条件只支持 = 的条件,不支持 <= >= != 等判断。 同时在某些严谨性上没有做校验,例如 setStringMap 和 setRawMap 是不能同时为空的,否则 update 语句就失去意义了。

总结

通过以上的例子也可以总结出建造者模式要解决什么样的问题:

  • 需要在生成最终的实例前进行参数的合法性校验
  • 避免出现无效的中间态对象。

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

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

相关文章

自动化释放5G全部潜力:新西兰电信One NZ的实践之路

本文详细介绍了新西兰电信运营商One New Zealand如何通过自动化技术实现5G网络转型,包括容器化平台、GitOps自动化流程和AI驱动的运维框架,大幅提升部署效率和网络可靠性。电信行业正站在新时代的门槛上。随着5G网络…

实用指南:C++设计模式_创建型模式_原型模式Prototype

实用指南:C++设计模式_创建型模式_原型模式Prototype2025-10-22 18:06 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; di…

第二十一篇

今天是10月22号,上了离散和马原。

DEIMv2浅读

DEIMv2代表了实时目标检测领域的一次重大飞跃,它通过空间调谐适配器(STA)、双轨制骨干网络设计、高效解码器优化等创新技术,成功地将DINOv3强大的视觉表示能力引入到对计算资源敏感的实时检测任务中,在精度和效率的平…

阿里出手了:全免费!号称国内版ClaudeCode?

这两年编程的开发工具层出不穷,也是一片红海,前有 Claude、CodeX,后有 Code Buddy、Qwen、Qoder 等。那问题来了,有没有一款好用、且免费的编程工具呢? 那么今天给大家分享 3 款好用且免费的编程工具。 视频分享…

[MS-DOS]MS-DOS 6.22 with CD-ROM Driver.ver.6.22.English下载与安装

下载地址: https://archive.org/details/ms-dos-6.22-with-cd-rom-driver.ver.6.22.english MS-DOS 6.22 with CD-ROM Driver.ver.6.22.English安装视频: How to Install MS-DOS on VirtualBox https://www.youtube.…

2025 年国内品牌设计公司最新推荐排行榜:聚焦行业领军者优势,精选优质服务商深度解析

引言 在当前激烈的市场竞争中,品牌设计已成为企业塑造独特形象、提升核心竞争力的关键环节。然而,品牌设计行业乱象丛生,部分企业缺乏专业体系、过度追求短期利益,导致企业难以找到靠谱的合作伙伴。为帮助企业精准…

报考PostgreSQL中级认证证书多少钱?

近几年随着PostgreSQL数据库在国内的流行,考相关认证的人也多了起来,尤其是使用PostgreSQL数据库的企业,不少员工想了解一下关于PostgreSQL数据库认证的费用、时间、题型、难度等问题,这里做些收费方面的介绍。 首…

087_尚硅谷_switch使用细节(1)

087_尚硅谷_switch使用细节(1)1.swithc细节讨论 2.case和switch 后是一个表达式(即 常量值、变量、一个有返回值的函数等都可以) 3.1.case 后的各个表达式的值的数据类型, 必须和 switch 的表达式数据类型一致,否则程…

linux服务器操作系统字符集是GBK,tomcat和部署的程序是UTF-8,启动后应用界面乱码如何解决

当 Linux 系统编码为 GBK,而 Tomcat 使用 UTF-8 时,程序界面乱码的核心原因是编码 / 解码环节不统一(如 JVM 默认编码、请求响应编码、资源文件编码等不一致)。解决需从多个层面统一编码为 UTF-8,具体步骤如下: …

2025 年感温电缆厂家最新推荐权威榜单重磅发布,全方位解析头部品牌优势助力工业消防精准选型

在工业生产、城市基建等领域,火灾安全防护是保障生命财产安全的核心环节,而感温电缆作为火灾早期探测的关键设备,其品质与性能直接决定防护效果。当前感温电缆市场品牌繁杂,既有深耕多年的老牌企业,也有新兴入局者…

完整教程:2- 十大排序算法(希尔排序、计数排序、桶排序)

完整教程:2- 十大排序算法(希尔排序、计数排序、桶排序)pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consol…

预测(笔记)

概念 利用历史数据,推断未来不确定因素的方法。这是基于:过去的规律,一定会在未来不断地发生。可以应用在销售数据、经济走势、产量范围、劳动力需求等。在项目PERT中,我们用均值、方差计算出某个时间完成的概率,…

分词器模型

中文分词是NLP中一个独特且富有挑战性的任务,因为中文文本没有像英文空格那样的天然词语边界。 现代分词器模型(尤其是基于Transformer的模型如BERT、GPT等使用的中文分词器)主要采用子词分词算法,但其处理方式与英…

Windows Server 2025 安装IIS服务

2 3 3、在弹出的添角色和功能向导中选择下一步 4、选择:基于角色或基于功能的安装,然后下一步 5、选择:从服务器池中选择服务器,然后下一步 6、选择:Web服务器(IIS),在弹出的对话框选择添加功能 7、确保web服…

易路薪酬能力深度解析:以科技赋能企业薪酬管理新范式

前言:中国薪酬管理的数字化变革与易路的核心价值 在当前数字经济浪潮的推动下,中国企业的薪酬管理正经历一场前所未有的深刻变革。传统的薪酬管理模式,因其固有的效率低下、数据孤立、决策滞后等弊端,已难以适应瞬…

LaTeX 项目结构优化:从基础到专业

LaTeX 项目结构优化:从基础到专业LaTeX 项目结构优化:从基础到专业 在上一篇文章《在 VS Code 中集成 LaTeX 环境并创建第一个文档》中,我们介绍了如何搭建基础的 LaTeX 开发环境。本文将进一步探讨如何将简单的 La…

Java的优势有哪些

Java 的价值 当具有开创性的 Java 白皮书在 1995 年推出该语言时,它列出了七项使其超越竞争对手的核心价值。如今,Java 为在 AWS 和 Google Cloud 等主要云上运行的大规模系统提供动力,这使得这些价值对于现代部署和…

今日开启!飞书 燕千云年终钜惠活动来袭

从今日开启,燕千云联合飞书推出年终限时钜惠推广活动,新老用户皆可获得权益。燕千云携手飞书,共创高效智能的企业IT服务新时代!燕千云此次携手飞书发起联合推广活动,共同助力企业提升内部IT服务体验与协同效率。从…

2025 年 LFT 材料源头厂家最新推荐权威榜单:复合 / 注塑 / 增强 / 轻量化 / 长碳纤 / 长玻纤 / 耐高温 LFT 材料优质公司推荐

当前 LFT 材料在汽车、新能源、航空航天等领域的应用愈发广泛,市场需求持续攀升,但行业乱象却让众多采购方和合作企业陷入选择困境。一方面,市场上 LFT 材料厂家数量繁杂,部分厂家缺乏核心生产技术,产品性能波动大…