[设计模式] 建造者模式

一、引言

起因是学习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

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

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

相关文章

视频特效编辑软件 After Effects 2022 mac中文版介绍 (ae 2022)

After Effects 2022 mac是一款视频特效编辑软件&#xff0c;被称为AE&#xff0c;拥有强大的特效工具&#xff0c;旋转&#xff0c;用于2D和3D合成、动画制作和视觉特效等&#xff0c;效果创建电影级影片字幕、片头和过渡&#xff0c;是一款可以帮助您高效且精确地创建无数种引…

2009-2018年全国各省财政透明度数据

2009-2018年全国各省财政透明度数据 1、时间&#xff1a;2009-2018年 2、指标&#xff1a;财政透明度 3、范围&#xff1a;31省 4、来源&#xff1a;财政透明度报告 5、指标解释&#xff1a; 财政透明度是公开透明的重要方面&#xff0c;体现了现代预算制度和法治政府的特…

GoLong的学习之路(二十二)进阶,语法之并发(go最重要的特点)(channel的主要用法)

这一章是接上一章内容继续&#xff0c;上一章说到协程也就是goroutine&#xff0c;如何使用它&#xff0c;这一张是讲一种数据结构。当然这个章节的数据结构非常重要。可以说这个数据结构就是为了方便协程&#xff0c;才制作出来的。 单纯地将函数并发执行是没有意义的。函数与…

Tomcat的类加载器

详情可以参考&#xff1a;https://tomcat.apache.org/tomcat-10.1-doc/class-loader-howto.html 简要说明 Tomcat安装了多种类加载器&#xff0c;以便容器的不同部分、容器中的应用访问能够不同的类和资源。 在Java环境中&#xff0c;类加载器被组织为父-子树的形式。通常情况…

GaN HEMT 电容的分析建模,包括寄生元件

标题&#xff1a;Analytical Modeling of Capacitances for GaN HEMTs, Including Parasitic Components 来源&#xff1a;IEEE TRANSACTIONS ON ELECTRON DEVICES&#xff08;14年&#xff09; 摘要&#xff1a;本文提出了一种基于表面势的终端电荷和电容模型&#xff0c;包…

【Git】Git安装入门使用常用命令Gitee远程仓库上传文件与下载

一&#xff0c;Git入门 1.1 Git是什么 Git是一款分布式版本控制系统&#xff0c;被广泛用于软件开发中的源代码管理。它由Linus Torvalds在2005年创造并发布&#xff0c;旨在解决传统版本控制系统&#xff08;如SVN&#xff09;的一些局限性。主要用于敏捷高效地处理任何或小或…

数组的存储结构、特殊矩阵和稀疏矩阵的压缩存储

数组的存储结构、特殊矩阵和稀疏矩阵的压缩存储 1.数组的存储结构、特殊矩阵、稀疏矩阵的压缩存储1.1 数组的存储结构1.1.1 一维数组的存储结构关系式1.1.2 多维数组的存储结构关系式 1.2 特殊矩阵的压缩存储1.2.1 对称矩阵1.2.2 下三角矩阵1.2.3 上三角矩阵1.2.4 三对角矩阵 1…

【MySQL】库操作和表操作

文章目录 一、库操作1. 创建数据库2. 数据库的编码问题查看系统默认支持的字符集查看数据库支持的字符集查看数据库支持的字符集校验规则校验规则对数据库的影响 3. 操纵数据库查看数据库显示创建语句修改数据库删除数据库 4. 数据库的备份和恢复数据库的备份数据库的恢复表的删…

二分详解(整数二分、浮点二分,附加例题)

一、二分 1.二分性质 二分的本质&#xff1a;二分可以将边界点二分出来&#xff08;即一部分满足这个条件&#xff0c;一部分不满足这个条件&#xff09; 二分的时候也一定有解 2.整数二分 1.寻找红色边界点x int l0,rn-1; while(l<r) {int mid(lr1)/2;if(check(mid))l…

MATLAB中Stem3函数用法

目录 语法 说明 向量和矩阵数据 表数据 其他选项 示例 行向量输入 列向量输入 矩阵输入 使用向量输入指定针状线条位置 使用矩阵输入指定针状线条位置 填充标记 线型、标记符号和颜色选项 线型、标记符号和颜色选项 其他样式选项 绘制表中的数据 特定坐标区上…

JS逆向爬虫---请求参数加密②【某麦数据analysis参数加密】

主页链接: https://www.qimai.cn/rank analysis逆向 完整参数生成代码如下&#xff1a; const {JSDOM} require(jsdom) const dom new JSDOM(<!DOCTYPE html><p>hello</p>) window dom.windowfunction customDecrypt(n, t) {t t || generateKey(); //…

kubernetes集群编排——k8s资源监控

资源限制 上传镜像 [rootk8s2 limit]# vim limit.yaml apiVersion: v1 kind: Pod metadata:name: memory-demo spec:containers:- name: memory-demoimage: stressargs:- --vm- "1"- --vm-bytes- 200Mresources:requests:memory: 50Milimits:memory: 100Mi [rootk8s2…

竞赛选题 深度学习机器视觉车道线识别与检测 -自动驾驶

文章目录 1 前言2 先上成果3 车道线4 问题抽象(建立模型)5 帧掩码(Frame Mask)6 车道检测的图像预处理7 图像阈值化8 霍夫线变换9 实现车道检测9.1 帧掩码创建9.2 图像预处理9.2.1 图像阈值化9.2.2 霍夫线变换 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分…

Vim编辑器学习

B站学习vim指令链接 1&#xff1a;vim下有两种模式&#xff0c;一种是命令模式&#xff0c;一种是编辑模式 2&#xff1a;命令到编辑模式&#xff0c;按键盘i&#xff0c;编辑到命令格式按Esc 3&#xff1a;&#xff1a;wq 保存并退出 &#xff1a;wq code.c保存并把文件命名为…

ubuntu, nvidia driver, cuda, cudnn, pytorch-gpu版本安装

文章目录 1.常用指令1.1查看cpu是intel还是amd:1.2.查看ubuntu版本1.3.查看架构1.4.查看已安装的nvidia驱动1.5.进入tty模式 2.安装ubuntu22.04 和 nvidia 驱动3.ubuntu 安装 anaconda4.安装pytorch gpu版本5.安装完整版cuda 和 cudnn6.nvidia-driver, cuda-toolkit, cudnn 1.常…

AI:66-基于机器学习房价预测

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌在这个漫长的过程,中途遇到了不少问题,但是…

机器视觉的试卷批改系统 - opencv python 视觉识别 计算机竞赛

文章目录 0 简介1 项目背景2 项目目的3 系统设计3.1 目标对象3.2 系统架构3.3 软件设计方案 4 图像预处理4.1 灰度二值化4.2 形态学处理4.3 算式提取4.4 倾斜校正4.5 字符分割 5 字符识别5.1 支持向量机原理5.2 基于SVM的字符识别5.3 SVM算法实现 6 算法测试7 系统实现8 最后 0…

【算法设计】回溯法算法设计——骑士游历问题(C++实现)

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 更多算法分析与设计知识专栏&#xff1a;算法分析&#x1f525; 给大家跳…

【1++的Linux】之线程(三)含生产者消费者模型

&#x1f44d;作者主页&#xff1a;进击的1 &#x1f929; 专栏链接&#xff1a;【1的Linux】 文章目录 一&#xff0c;可重入与线程安全二&#xff0c;死锁三&#xff0c;线程同步什么是线程同步&#xff1f;怎么实现线程同步条件变量 四&#xff0c;生产者与消费者模型1&…

C++ Qt 学习(三):无边框窗口设计

1. 无边框窗口 1.1 主窗口实现 MainWidget.h #pragma once#include <QtWidgets/QWidget> #include "CTitleBar.h" #include "CFrameLessWidgetBase.h"// 主窗口 MainWidget 继承自无边框窗口公用类 CFrameLessWidgetBase class MainWidget : publi…