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

写在前面

不知道,你在工作中有没有使用过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的那个…

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

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

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

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

Matplotlib是什么

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

HTTP1.1 wireshark分析

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

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

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

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

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

【Linux】分布式监控 Zabbix

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

Python+Requests+Excel接口测试实战

1、EXCEL文件接口保存方式,如图。 2、然后就是读取EXCEL文件中的数据方法,如下: 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 路径:device / {vendor-name} / {platform-name} / {device-name} / system / bootanimation.zip 例:android \ device \ softwinner \ ceres \ ceres-b6 \ system \ bootanimation.zip android13 路径:device / softwinner / {PRO…

MyBatis全篇

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

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

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

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

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

大屏项目也不难

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

网络编程 socket

目录 网络编程 套接字(socket)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下指令都是一致的 指令 经过 解释器 把每一条指令 解释成 机器码…

SpringBoot项目从0到1配置logback日志打印

大家好!我是sum墨,一个一线的底层码农,平时喜欢研究和思考一些技术相关的问题并整理成文,限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。 以下是正文! 一、写文背景 我们在写后端…

安卓进度条:ProgressBar和Seekbar

一、ProgressBar进度条介绍 ProgressBar 是 Android 中的一个进度条控件,用于显示正在进行的任务的进度。它可以以水平或圆形的形式展示进度,并提供了多种样式和属性来满足不同的需求。 相关属性: android:progress:设置进度条的…

计数排序

计数排序 排序步骤 1、以最大值和最小值的差值加一为长度创建一个新数组 2、将索引为0对应最小值,索引为1对应最小值1,索引为2对应最小值2,以此类推,将索引对应最小值到最大值之间所有的值 3、遍历一遍,遇到一个数字…

计算机网络 - http协议 与 https协议(2)

前言 本篇介绍了构造http请求的的五种方式,简单的使用postman构造http请求,进一步了解https, 学习https的加密过程,了解对称密钥与非对称密钥对于加密是如何进行的,如有错误,请在评论区指正,让我们一起交流…