设计模式之建造者设计模式

写在前面

不知道,你在工作中有没有使用过lombok,如果你使用过,不知道你有没有使用过其中的@Builder注解,其就会帮我们生成建造者设计模式相关的代码,本文就一起来看下吧!

1:介绍

1.1:什么时候使用建造者设计模式

当一个对象的属性很多,并且在不同的场景下对象创建时需要初始化的属性不同时,可以考虑使用该设计模式,否则就需要创建大量的构造函数,造成代码的臃肿和难以维护,并且对于使用者来说,到底选择哪个构造函数来初始化也会比较麻烦。

1.2:UML类图

工厂方法设计模式,包含如下元素:

1:产品待创建的对象
2:抽象构造者定义创建产品需要设置的属性
3:具体构造者具体产品的构造者,继承抽象构造者,负责设置具体的信息
4:导演使用构造者来创建产品

如下图:

在这里插入图片描述

2:实例

源码 。

2.1:场景

按照不同的需求创建手机对象。

2.2:程序

  • 创建产品类
@Data // 可自动生成get、set、toString方法
public class Phone {/*** 品牌*/private String brand;/*** 操作系统*/private String os;/*** 内存大小, Unit: GB*/private Integer ramSize;/*** 售价*/private Double price;
}
  • 创建抽象构造器
/*** 手机抽象建造者的接口*/
public interface PhoneBuilder {/*** 设置品牌*/void setBrand();/*** 设置操作系统*/void setOs();/*** 设置内存大小*/void setRamSize();/*** 设置售价*/void setPrice();/*** 获取 Phone 实例* @return*/Phone getPhone();
}
  • 创建两个具体的构造器
    分别创建预定好的苹果手机,和小米手机:
/*** 苹果手机建造者*/
public class IPhoneBuilder implements PhoneBuilder{private Phone phone;public IPhoneBuilder() {phone = new Phone();}@Overridepublic void setBrand() {phone.setBrand("Apple");}@Overridepublic void setOs() {phone.setOs("IOS");}@Overridepublic void setRamSize() {phone.setRamSize(2);}@Overridepublic void setPrice() {phone.setPrice(6666.66);}@Overridepublic Phone getPhone() {return phone;}
}/*** 小米手机建造者*/
public class MiPhoneBuilder implements PhoneBuilder {private Phone phone;public MiPhoneBuilder() {phone = new Phone();}@Overridepublic void setBrand() {phone.setBrand("Xiao Mi");}@Overridepublic void setOs() {phone.setOs("Android");}@Overridepublic void setRamSize() {phone.setRamSize(8);}@Overridepublic void setPrice() {phone.setPrice(1999.99);}@Overridepublic Phone getPhone() {return phone;}
}
  • 创建导演类
/*** 导演: 负责指定手机建造流程*/
public class PhoneDirector {/*** 导演指挥建造者完成手机的建造工作* @param phoneBuilder*/public void construct(PhoneBuilder phoneBuilder) {phoneBuilder.setBrand();phoneBuilder.setOs();phoneBuilder.setRamSize();phoneBuilder.setPrice();}
}
  • 测试
@Test
public void origin() {// 1. 创建一个导演PhoneDirector phoneDirector = new PhoneDirector();/***** 2. 建造 iPhone 手机 *****/// 2a. 创建一个 iPhone建造者IPhoneBuilder iPhoneBuilder = new IPhoneBuilder();// 2b. 导演指导 iPhone建造者 来建造一个iPhone的实例phoneDirector.construct(iPhoneBuilder);// 2c. 从 iPhone建造者 中获取实例Phone iPhone = iPhoneBuilder.getPhone();System.out.println(iPhone);/***** 3. 建造 Xiao Mi Phone 手机 *****/// 3a. 创建一个 MiPhone建造者MiPhoneBuilder miPhoneBuilder = new MiPhoneBuilder();// 3b. 导演指导 MiPhone建造者 来建造一个MiPhone的实例phoneDirector.construct(miPhoneBuilder);// 3c. 从 MiPhone建造者 中获取实例Phone miPhone = miPhoneBuilder.getPhone();System.out.println(miPhone);}

运行:

Phone(brand=Apple, os=IOS, ramSize=2, price=6666.66)
Phone(brand=Xiao Mi, os=Android, ramSize=8, price=1999.99)Process finished with exit code 0

需要注意到,这里客户端虽然不需要去设置对象的各种属性信息了,但是仅仅适用于要设置的属性都是确定的,并且要设置的属性值也是确定的场景,如果是要设置哪些属性是不确定,要设置的属性值也是不确定的话,这种方式明显就不使用了,怎么做呢?可以创建对应的构造函数,直接使用构造函数创建,但是可能需要创建非常多的构造函数,会让代码变的臃肿且难以维护。也可以调用无参构造函数,然后分别调用对应的setXxx方法,但是这样程序会变的复杂,且效率低下,因此就有了建造者设计模式的简化版本,这种方式创建一个内部的静态Builder类,之后通过链式调用的方式来设置属性,最终调用build,build内会调用对象的全部参数的构造函数,从而完成对象创建,使用简化版本的建造者设计模式修改Phone类如下:

/*** 手机*/
@ToString
public class Phone {/*** 品牌*/private String brand;/*** 操作系统*/private String os;/*** 内存大小, Unit: GB*/private Integer ramSize;/*** 售价*/private Double price;/*** 提供一个静态方法以方便创建一个Phone建造者实例* @return*/public static Phone.PhoneBuilder builder() {return new Phone.PhoneBuilder();}/*** 提供一个Phone的全参构造器以供建造者Builder来建造Phone实例* @param brand* @param os* @param ramSize* @param price*/public Phone(String brand, String os, Integer ramSize, Double price) {this.brand = brand;this.os = os;this.ramSize = ramSize;this.price = price;}/*** 静态内部类: Phone Builder 建造者*/public static class PhoneBuilder {private String brand;private String os;private Integer ramSize;private Double price;/*** Builder 建造者构造器*/public PhoneBuilder() {}public Phone.PhoneBuilder brand(String brand) {this.brand = brand;return this;}public Phone.PhoneBuilder os(String os) {this.os = os;return this;}public Phone.PhoneBuilder ramSize(Integer ramSize) {this.ramSize = ramSize;return this;}public Phone.PhoneBuilder price(Double price) {this.price = price;return this;}/*** 建造者通过 Phone的全参构造器 来构造 Phone 实例* @return*/public Phone build() {return new Phone(brand, os, ramSize, price);}}
}

测试:

@Test
public void simplify() {dongshi.daddy.builder.simplify.Phone P40Pro= dongshi.daddy.builder.simplify.Phone.builder()  // 通过产品的静态方法获取建造者.brand("华为")         // "客户"(调用方)充当了Director这个角色.os("鸿蒙").ramSize(12).price(9999.99).build();             // 获得建造完成的产品System.out.println(P40Pro);
}

输出:

Phone(brand=华为, os=鸿蒙, ramSize=12, price=9999.99)Process finished with exit code 0

这种简化版本的构造者设计模式其实就是我们在工作中经常用的lombok 的@Builder注解,如下:

@Builder
@ToString
public class PhoneWithLombok {/*** 品牌*/private String brand;/*** 操作系统*/private String os;/*** 内存大小, Unit: GB*/private Integer ramSize;/*** 售价*/private Double price;
}

程序是不是简洁多了,工作中用起来吧!

测试:

@Test
public void simplifyWithLombok() {PhoneWithLombok P40Pro= PhoneWithLombok.builder()  // 通过产品的静态方法获取建造者.brand("华为lombok")         // "客户"(调用方)充当了Director这个角色.os("lombok 操作系统").ramSize(12).price(9999.99).build();             // 获得建造完成的产品System.out.println(P40Pro);
}

输出:

PhoneWithLombok(brand=华为lombok, os=lombok 操作系统, ramSize=12, price=9999.99)Process finished with exit code 0

写在后面

参考文章列表

GoF设计模式(五):Builder Pattern 建造者模式 。

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

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

相关文章

spark启动HA时workers为0,且为standby状态

今天学习一个spark视频,在启动StandAloneHa模式的时候,发现workers为0,而且spark两个master的状态都为standby,找了很久,才知道我用的spark3.2 最低支撑的zookeeper版本为3.5.x,而且zookeeper的安装包是需要带bin的那个…

Selenium 获取接口响应数据

目录 前言 seleniumwire简介 功能 兼容性 目录 安装 创建webdriver 获取请求 请求对象 限制请求捕获 前言 有时候需要知道UI界面操作的同时接口响应数据是否正常,这时就需要获取接口响应数据。Selenium本身没有获取接口响应的api,但是可以通过…

我的创作纪念日

机缘 实战项目中的经验分享:多看文档!多看书!团队中有这领域的前辈多去问,但是请带上自己的思考和间接,不要问一些文档中早已写过的内容。日常学习过程中的记录: 关注行业动态:我在日常学习过程…

1063 Set Similarity (PAT甲级)

大半个月没做题&#xff0c;手非常生... #include <cstdio> #include <vector> #include <map>int N, M, K, t, m, n; std::vector<std::map<int, int>> vec;void similarity(int a, int b){int cnt 0;for(auto c : vec[a]){if(vec[b].find(c.…

CSS3 动画 animation 入门学习笔记 之 属性详解

文章目录 简单介绍 CSS 动画CSS 动画的作用CSS 动画语法介绍CSS 动画属性animation-nameanimation-durationanimation-delayanimation-directionanimation-iteration-countanimation-play-stateanimation-timing-functionanimation-fill-modeanimation 简单介绍 CSS 动画 引用…

基于深度学习的高精度80类动物目标检测系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度80类动物目标检测识别系统可用于日常生活中或野外来检测与定位80类动物目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的80类动物目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系统采用YO…

Matplotlib是什么

Matplotlib 是一款用于数据可视化的 Python 软件包&#xff0c;支持跨平台运行&#xff0c;它能够根据 NumPy ndarray 数组来绘制 2D 图像&#xff0c;它使用简单、代码清晰易懂&#xff0c;深受广大技术爱好者喜爱。 NumPy 是 Python 科学计算的软件包&#xff0c;ndarray 则…

HTTP1.1 wireshark分析

目录 http1.1wireshark分析http 1.1 keep-alive的2次http请求wireshark分析http1.1 keep-alive过期的2次请求keep-alive报文 本地springboot启动一个简单的服务&#xff0c;然后请求测试 tcpdump -i lo0 -nnvv -w tmp.cap tcpdump 本地回环网卡 http1.1 HTTP/1.0 每进行一次…

蚂蚁集团开源可信隐私计算框架「隐语」:开放、通用

7 月 4 日,蚂蚁集团宣布面向全球开发者正式开源可信隐私计算框架 “隐语”。 隐语是蚂蚁集团历时 6 年自主研发,以安全、开放为核心设计理念打造的可信隐私计算技术框架,涵盖了当前几乎所有主流隐私计算技术。 据介绍,隐语内置 MPC、TEE、同态等多种密态计算虚拟设备,提…

操作系统练习:创建内核模块,并加载和卸载模块

说明 本文记录如何创建和编译一个内核模块&#xff0c;以及加载和卸载内核模块。为《操作系统概念(第九版)》第二章&#xff0c;关于“Linux内核模块”的练习题。 创建内核模块 注&#xff1a;我这里是基于阿里云的轻量应用服务器&#xff08;即当前博客服务器&#xff09; 首…

【Linux】分布式监控 Zabbix

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 Zabbix 介绍zabbix 概述Zabbix 监控原理Zabbix 6.0 新特性Zabbix 6.0 功能组件 Zabbix 6.0 部署Zabbix 添加客户端主机Zabbix 自定义监控内容Zabbix 自动发现与自动…

Python+Requests+Excel接口测试实战

1、EXCEL文件接口保存方式&#xff0c;如图。 2、然后就是读取EXCEL文件中的数据方法&#xff0c;如下&#xff1a; 1 import xlrd2 3 4 class readExcel(object):5 def __init__(self, path):6 self.path path7 8 property9 def getSheet(self): 10 …

android更换开机动画

android11 路径&#xff1a;device / {vendor-name} / {platform-name} / {device-name} / system / bootanimation.zip 例&#xff1a;android \ device \ softwinner \ ceres \ ceres-b6 \ system \ bootanimation.zip android13 路径&#xff1a;device / softwinner / {PRO…

MyBatis全篇

文章目录 MyBatis特性下载持久化层技术对比 搭建MyBatis创建maven工程创建MyBatis的核心配置文件创建mapper接口创建MyBatis的映射文件测试功能加入log4j日志功能加入log4j的配置文件 核心配置文件的完善与详解MyBatis的增删改查测试功能 MyBatis获取参数值在IDEA中设置中配置文…

《TCP/IP网络编程》第3,4章学习记录

基础知识&#xff1a; struct sockaddr_in {sa_family_t sin_family; //地址族&#xff08;Address Family)uint16_t sin_port; //16位TCP/UDP端口号struct in_addr sin_addr; //32位IP地址char sin_zero[8]; //不使用 }sa_family_t包括&#xff1a; (1)AF_INET,IPv4网络协议…

Linux宝塔Mysql读写分离配置,两台服务器,服务器存在多个库

Linux宝塔Mysql读写分离配置&#xff0c;两台服务器&#xff0c;服务器存在多个库 一、主库操作 #登录数据库&#xff0c;用root登录方便&#xff0c;用其他账号会提示权限不足&#xff0c;需要登录root给予权限 mysql -u root -p 密码#创建一个账号&#xff0c;供从库用该账…

大屏项目也不难

项目环境搭建 使用create-vue初始化项目 npm init vuelatest准备utils模块 业务背景&#xff1a;大屏项目属于后台项目的一个子项目&#xff0c;用户的token是共享的 后台项目 - token - cookie 大屏项目要以同样的方式把token获取到&#xff0c;然后拼接到axios的请求头中…

网络编程 socket

目录 网络编程 套接字&#xff08;socket&#xff09;1. 认识端口号2. TCP协议3. UDP协议4. 网络字节序列5. 常见的套接字6. socket编程接口6.1 socket常见APIsocket函数recvfrom函数sendto函数read函数 从tcp socket中读取接收数据 6.2 sockaddr结构6.3 地址转换函数6.4 udp s…

JVM内存结构—— 程序计数器,虚拟机栈 解析

JVM的内存结构 1. 程序计数器(PC Register )寄存器 1.1 全称:Program Counter Register 1.2 作用 首先,java源代码 被 编译成 二进制的 字节码 (jvm指令) jvm跨平台就是这一套指令,linux 下,windows下指令都是一致的 指令 经过 解释器 把每一条指令 解释成 机器码…

[运维] caddy 介绍

Caddy 是一个开源的跨平台服务器软件&#xff0c;旨在提供简单易用的配置和自动化的 HTTPS 功能。以下是关于 Caddy 的一些介绍&#xff1a; 简单易用&#xff1a;Caddy 设计的目标之一是提供简单易用的配置和操作体验。它采用简洁的 Caddyfile 语法&#xff0c;使你能够快速而…