当前位置: 首页 > news >正文

[设计模式] 建造者模式

一、引言

起因是学习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.lryc.cn/news/226395.html

相关文章:

  • 在DDD领域驱动下的微服务数据库的MVC设计思路(高度可行性)
  • Leetcode2834. 找出美丽数组的最小和
  • acwing算法基础之搜索与图论--kruskal算法
  • 微信H5跳转微信小程序
  • Yii2 引入 外部无命名空间的类,Class not found
  • 设计模式是测试模式咩?
  • Aspose.OCR for .NET 2023Crack
  • conda环境中pytorch1.2.0版本安装包安装一直失败解决办法!!!
  • 后端面试问题(学习版)
  • 数据管理系统-week1-介绍
  • 【SpringBoot】手写模拟SpringBoot核心流程
  • 应对.locked勒索病毒:恢复、预防全方位攻略
  • 基于DS1302时钟液晶12864显示2路闹钟仿真及源程序
  • AGC034E Complete Compress
  • python设计模式12:状态模式
  • JS对图片尺寸和DPI进行编辑修改(1寸照修改为2寸照)
  • EDA实验----四选一多路选择器设计(QuartusII)
  • 从windows iso文件中提取install.wim
  • Python的flask网页编程的GET和POST方法的区别
  • 15 # 手写 throttle 节流方法
  • puzzle(1612)拼单词、wordlegame
  • 【解决方案】pytion 运行时提示 import psutil ModuleNotFoundError: No module named ‘psutil‘
  • CSS3 过度效果、动画、多列
  • java使用geotools解析矢量数据kml、geojson、shp文件
  • 原生 JS DOM 常用操作大全
  • 昇腾CANN 7.0 黑科技:DVPP硬件加速训练数据预处理,友好解决Host CPU预处理瓶颈
  • Aria2 任意文件写入漏洞复现
  • 思维模型 多看效应
  • 持续集成交付CICD:Jenkins Pipeline与远程构建触发器
  • 【无标题(PC+WAP)花卉租赁盆栽绿植类pbootcms站模板