[设计模式] 建造者模式
一、引言
起因是学习okhttp过程中遇到的这段代码
Request request = original.newBuilder().url(original.url()).header("Authorization", "Bearer " + BearerTokenUtils.getToken(configuration.getApiKey(), configuration.getApiSecret())).header("Content-Type", Configuration.JSON_CONTENT_TYPE).header("User-Agent", Configuration.DEFAULT_USER_AGENT).header("Accept", Configuration.SSE_CONTENT_TYPE).method(original.method(), original.body()).build();
newBuilder().build()创建对象的方式以前没注意过,搜了一下,这种方法属于广义上的建造者模式,于是进行学习。
二、介绍
我自己概括建造者模式的思想:把一个整体A,分割成多个部分A1,A2,A3,A4。每个部分分开建造,最后组装成一个整体。
设想这样的场景,装修一个房子,需要考虑吊顶、涂料、地板、瓷砖等部分。每个部分都有不同的品牌选择。
装修公司提供一些成套的、整体装修方案,可以看作是这些部分的排列组合。
三、代码
我们用代码进行描述。以下是代码结构
模块builder-pattern-01中,Matter是材料的父接口,定义了材料的各种属性。
public interface Matter {/*** 场景:ceiling、coat、floor、tile* @return 吊顶、涂料、地板、瓷砖*/String scene();/*** 品牌* @return 自定字符串*/String brand();/*** 型号* @return 自定义字符串*/String model();/*** 单价(平米报价)* @return BigDecimal*/BigDecimal price();/*** 描述* @return 自定义字符串*/String desc();
}
ceiling、coat、floor、tile分别表示四个部分:吊顶、涂料、地板、瓷砖。
这四个部分有不同的材料。这些材料组合装修,成为一个整体方案。代码略。
builder-pattern-02中,IMenu是装修包接口,用于添加材料;DecoratiuonPackageMenu是其实现。
/*** 装修包接口* 用于添加材料*/
public interface IMenu {/*** 添加吊顶* @param matter 材料* @return 装修包*/IMenu appendCeiling(Matter matter);/*** 添加涂料* @param matter 材料* @return 装修包*/IMenu appendCoat(Matter matter);/*** 添加地板* @param matter 材料* @return 装修包*/IMenu appendFloor(Matter matter);/*** 添加瓷砖* @param matter 材料* @return 装修包*/IMenu appendTile(Matter matter);/*** 获取装修包信息* @return 装修包detail*/String getDetail();
}
public class DecorationPackageMenu implements IMenu{/** 材料清单 */private List<Matter> list = new ArrayList<>(16);/** 总价格 */private BigDecimal price = BigDecimal.ZERO;/** 面积 */private BigDecimal area;/** 装修等级 */private String grade;public DecorationPackageMenu(){}public DecorationPackageMenu(double area, String grade) {this.area = new BigDecimal(area);this.grade = grade;}@Overridepublic IMenu appendCeiling(Matter matter) {list.add(matter);// 注意这个price=赋值操作,单纯的add等运算不会改变原来的值// price = price + ( area * (unitPrice * matter.price()) )price = price.add(area.multiply(matter.price()));return this;}@Overridepublic IMenu appendCoat(Matter matter) {list.add(matter);price = price.add(area.multiply(matter.price()));return this;}@Overridepublic IMenu appendFloor(Matter matter) {list.add(matter);price = price.add(area.multiply(matter.price()));return this;}@Overridepublic IMenu appendTile(Matter matter) {list.add(matter);price = price.add(area.multiply(matter.price()));return this;}@Overridepublic String getDetail() {StringBuilder detail = new StringBuilder("\r\n-------------------------------------------------------\r\n" +"装修清单: " + "\r\n" +"套餐等级: " + grade + "\r\n" +"套餐价格: " + price.setScale(2, BigDecimal.ROUND_HALF_UP) + "元\r\n" +"房屋面积: " + area.doubleValue() + "平方米\r\n" +"材料清单: " + "\r\n" );for (Matter matter : list) {detail.append("场景:").append(matter.scene()).append("、").append("品牌:").append(matter.brand()).append("、").append("类别:").append(matter.model()).append("、").append("平米价格:").append(matter.price()).append("元\r\n");}return detail.toString();}
}
builder是建造者,使用IMenu装修包对各种材料进行组装
public class Builder {public IMenu levelOne(Double area) {return new DecorationPackageMenu(area, "豪华欧式").appendCeiling(new LevelOneCeiling()).appendCoat(new DuluxCoat()).appendFloor(new DerFloor()).appendTile(new DongPengTile());}public IMenu levelTwo(Double area) {return new DecorationPackageMenu(area, "轻奢田园").appendCeiling(new LevelTwoCeiling()).appendCoat(new NipponCoat()).appendFloor(new ShengXiangFloor()).appendTile(new MarcoPoloTile());}public IMenu levelThree(Double area) {return new DecorationPackageMenu(area, "现代简约").appendCeiling(new LevelOneCeiling()).appendCoat(new NipponCoat()).appendFloor(new ShengXiangFloor()).appendTile(new DongPengTile());}
}
我们使用单元测试进行测试一下
@Testpublic void test_builder() {Builder builder = new Builder();System.out.println(builder.levelOne(132.5D).getDetail());System.out.println(builder.levelTwo(150.0D).getDetail());System.out.println(builder.levelThree(170.0D).getDetail());}
结果部分为
其余略
四、回归okhttp
建造者模式思想基本领略,那么okhttp的newBuilder().build()创建对象的思想跟上面差不多。
那么对于这段代码,
Request request = original.newBuilder().url(original.url()).header("Authorization", "Bearer " + BearerTokenUtils.getToken(configuration.getApiKey(), configuration.getApiSecret())).header("Content-Type", Configuration.JSON_CONTENT_TYPE).header("User-Agent", Configuration.DEFAULT_USER_AGENT).header("Accept", Configuration.SSE_CONTENT_TYPE).method(original.method(), original.body()).build();
我们可以进行学习newBuilder().build()这种创建对象的方式,先来看看其源码
public Builder newBuilder() {return new Builder(this);}public Request build() {if (this.url == null) {throw new IllegalStateException("url == null");} else {return new Request(this);}}public Builder url(HttpUrl url) {if (url == null) {throw new NullPointerException("url == null");} else {this.url = url;return this;}}public Builder header(String name, String value) {this.headers.set(name, value);return this;}public Builder method(String method, @Nullable RequestBody body) {if (method == null) {throw new NullPointerException("method == null");} else if (method.length() == 0) {throw new IllegalArgumentException("method.length() == 0");} else if (body != null && !HttpMethod.permitsRequestBody(method)) {throw new IllegalArgumentException("method " + method + " must not have a request body.");} else if (body == null && HttpMethod.requiresRequestBody(method)) {throw new IllegalArgumentException("method " + method + " must have a request body.");} else {this.method = method;this.body = body;return this;}}
通过newBuilder()创建了一个Builder类的对象,然后每次url() header()等方法修改属性,都是在对Builder类的对象进行修改,最后build()方法 通过Builder类的对象创建了Request对象。
这种基于不可变对象(或者为了不改变原对象)而创建新对象的方式也值得我们学习。
基于此思想,创建一个类Message
@Data
public class Message implements Serializable {private String role;private String content;private String name;public Message(){}public Message(Builder builder) {this.role = builder.role;this.content = builder.content;this.name = builder.name;}/*** 注意这里 static方法*/public static Builder builder() {return new Builder();}/*** 建造者模式*/public static final class Builder {private String role;private String content;private String name;public Builder(){}public Builder role(Constants.Role role) {this.role = role.getCode();return this;}public Builder content(String content) {this.content = content;return this;}public Builder name(String name) {this.name = name;return this;}public Message build() {return new Message(this);}}
}
得注意build()方法是在最后使用的,所以应该是Builder的方法,并且返回值类型应该是Message类。
补充,后来才发现lombok有@Builder注解,就是这个方法......
@Builder
相关文章:

[设计模式] 建造者模式
一、引言 起因是学习okhttp过程中遇到的这段代码 Request request original.newBuilder().url(original.url()).header("Authorization", "Bearer " BearerTokenUtils.getToken(configuration.getApiKey(), configuration.getApiSecret())).header(&quo…...
在DDD领域驱动下的微服务数据库的MVC设计思路(高度可行性)
在DDD领域驱动下的微服务架构中使用MVC设计思路来设计数据库是可行的,因为MVC是一种经典的软件架构模式,可以将应用程序分为三个主要部分:模型、视图和控制器。在微服务架构中,每个微服务可以看作是一个模块,可以使用M…...

Leetcode2834. 找出美丽数组的最小和
Every day a Leetcode 题目来源:2834. 找出美丽数组的最小和 解法1:贪心 从最小正整数 1 开始枚举,设当前数为 num,如果 nums 里没有 target - num,就说明可以添加 num,依次填满直到有 n 个数即可。 用…...
acwing算法基础之搜索与图论--kruskal算法
目录 1 基础知识2 模板3 工程化 1 基础知识 kruskal算法的关键步骤为: 将所有边按照权重从小到大排序。定义集合S,表示生成树。枚举每条边(a,b,c),起点a,终点b,边长c。如果结点a和结点b不连通(用并查集来…...
微信H5跳转微信小程序
官方文档:目录 | 微信开放文档 方法一:微信浏览器打开的h5跳转方式 HTML代码 <wx-open-launch-weapp id"launch-btn" username"所需跳转的小程序原始id" path"pages/pay/pay"><script type"text/wxtag…...
Yii2 引入 外部无命名空间的类,Class not found
记一次问题解决 问题描述 支付宝开放平台SDK v2 无命名空间。需 require 引入。 require Yii::$app->vendorPath."/alipay-sdk-php/v2/aop/AopClient.php"; var_dump(new AopClient([]));exit();上述写法会直接报错。 Class temporary\controllers\AopClient …...

设计模式是测试模式咩?
设计模式和测试模式概述 软件的生命周期为什么要进行测试(测试的目的)?软件的设计模式1. **瀑布模型**3. 增量和迭代模型4. 敏捷模型5. 喷泉模型 测试模型V模型W模型 一个应用程序从出生到“死亡”会经过非常漫长的流程…… 软件的生命周期 …...

Aspose.OCR for .NET 2023Crack
Aspose.OCR for .NET 2023Crack 为.NET在图片上播放OCR使所有用户和程序员都可以从特定的图像片段中提取文本和相关的细节,如字体、设计以及书写位置。这一特定属性为OCR的性能及其在扫描遵循排列的记录时的功能提供了动力。OCR的库使用一条线甚至几条线来处理这些特…...

conda环境中pytorch1.2.0版本安装包安装一直失败解决办法!!!
conda环境中pytorch1.2.0版本安装包安装一直失败解决办法 cuda10.0以及cudnn7.4现在以及安装完成,就差torch的安装了,现在torch我要装的是1.2.0版本的,安装包以及下载好了,安装包都是在这个网站里下载的(点此进入&…...

后端面试问题(学习版)
JAVA相关 JAVA语言概述 1. 一个".java"源文件中是否可以包含多个类?有什么限制? 可以。 一个源文件可以声明多个类,但是最多只能有一个类使用public进行声明 且要求声明public的类的类名与源文件相同。 2. Java的优势ÿ…...
数据管理系统-week1-介绍
文章目录 一、数据它是什么?二、电子存储设备三、持久存储设备1、硬盘驱动器(HDD)、硬盘、硬盘驱动器是一种用于存储和检索数字信息的机电持久存储设备。2、固态硬盘(SSD)使用非易失性存储器,即使用NAND闪存…...
【SpringBoot】手写模拟SpringBoot核心流程
依赖包 新建一个工程,包含两个 module: springboot 模块,表示 springboot 源码实现;user 模块,表示业务系统,使用 springboot 模块; 依赖包:Spring、SpringMVC、Tomcat 等ÿ…...
应对.locked勒索病毒:恢复、预防全方位攻略
导言: .locked勒索病毒并非简单的数字威胁,它是一场对个人和企业数字资产的精密审判。这种病毒通过各种方式感染系统,从而以瞬间之间将用户的关键文件变成数字拼图,无情地要求赎金以换取解锁的密钥。如果您正在经历勒索病毒数据恢…...

基于DS1302时钟液晶12864显示2路闹钟仿真及源程序
一、系统方案 1、本设计采用51单片机作为主控器。 2、DS1302采集年月日时分秒送到液晶12864显示。 3、按键年月日时分秒,两路闹钟。 二、硬件设计 原理图如下: 三、单片机软件设计 1、首先是系统初始化 uchar clock_time[6] {0X00,0X59,0X23,0X09,0X…...
AGC034E Complete Compress
AGC034E Complete Compress 洛谷[AGC034E] Complete Compress 题目大意 给你一棵有 n n n个节点的树,并用 01 01 01串告诉你哪些节点上有棋子(恰好一棵)。 你可以进行若干次操作,每次操作可以将两颗距离至少为 2 2 2的棋子向彼…...
python设计模式12:状态模式
什么是状态机? 关键属性: 状态和转换 状态: 系统当前状态 转换:一种状态到另外一种状态的变化。 转换由触发事件或是条件启动。 状态机-状态图 状态机使用场景: 自动售货机 电梯 交通灯 组合锁 停车计时…...
JS对图片尺寸和DPI进行编辑修改(1寸照修改为2寸照)
各种报名都对照片有大小限制,鉴于这种情况,网上搜了后拼凑出了如下代码,用于解决1寸照片修改为2寸照片,同时将DPI修改为300,当然也可以根据自己的情况修改代码: HTML <input type"file" id&…...

EDA实验----四选一多路选择器设计(QuartusII)
目录 一.实验目的 二.实验仪器设备 三.实验原理: 四.实验要求 五.实验内容及步骤 1.实验内容 2.实验步骤 六.实验报告 七.实验过程 1.创建Verilog文件,写代码 2.波形仿真 …...

从windows iso文件中提取install.wim
1、首先从微软官方下载需要的windows镜像 https://www.microsoft.com/zh-cn/software-download/windows10/ 2、在下载的iso文件右键,打开压缩包,在sources文件夹下,应该就可以看到install.wim了。但似乎在最新的win10版本,微软采…...
Python的flask网页编程的GET和POST方法的区别
关于flask网页编程的GET及POST方法之间存在哪些区别问题,我们主要从以下六个关键点予以详细阐述: 首先需要明确的是,GET与POST两种不同类型的HTTP方法所采用的请求模式有所差别。其中,GET方法采用的是基于URL请求的机制ÿ…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...

uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲
文章目录 前言第一部分:体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分:体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...

力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...