建造者设计模式

3. 建造者设计模式

3.1 原理

Builder 模式,中文翻译为建造者模式或者构建者模式,也有人叫它生成器模式

建造者模式是用来创建一种类型的复杂对象,通过设置不同的可选参数,“定制化”地创建不同的对象。

创建者模式主要包含以下四个角色:

  • 产品(Product):表示将要被构建的复杂对象。
  • 抽象创建者(Abstract Builder):定义构建产品的接口,通常包含创建和获取
    产品的方法。
  • 具体创建者(Concrete Builder):实现抽象创建者定义的接口,为产品的各个
    部分提供具体实现。
  • 指挥者(Director):负责调用具体创建者来构建产品的各个部分,控制构建过
    程。

我们以文档编辑器为例,假设我们需要创建一个复杂的HTML文档,它包含了标题、段落和图像等元素。我们可以使用创建者设计模式来构建HTML文档。

产品(Product)类 - HTML文档(HtmlDocument):

/*** 类描述:产品(Product),表示将要被构建的复杂对象。* 需求:创建一个复杂的HTML文档,它包含了标题、段落和图像等元素。我们可以使用创建者设计模式来构建HTML文档。** @Author crysw* @Version 1.0* @Date 2023/11/21 22:57*/
public class HtmlDocument {private String header;private String body;private String footer;public void addHeader(String header) {this.header = header;}public void addBody(String body) {this.body = body;}public void addFooter(String footer) {this.footer = footer;}@Overridepublic String toString() {return "<html><head>" + header + "</head><body>" + body + "</body><footer>"+ footer + "</footer></html>";}
}    

抽象创建者(Abstract Builder)类 - HtmlDocumentBuilder:

/*** 类描述:抽象创建者(Abstract Builder),定义构建产品的接口,通常包含创建和获取产品的方法。** @Author crysw* @Version 1.0* @Date 2023/11/21 23:01*/
public abstract class HtmlDocumentBuilder {/*** 文档对象*/protected HtmlDocument document;/*** 获取document文档对象** @return*/public HtmlDocument getDocument() {return document;}/*** 创建document文档对象*/public void createNewHtmlDocument() {document = new HtmlDocument();}/*** 构建文档对象的header*/public abstract void buildHeader();/*** 构建文档对象的body*/public abstract void buildBody();/*** 构建文档对象的footer*/public abstract void buildFooter();
}

具体创建者(Concrete Builder)类 - ArticleHtmlDocumentBuilder:

/*** 类描述:具体创建者(Concrete Builder),实现抽象创建者定义的接口,为产品的各个部分提供具体实现。** @Author crysw* @Version 1.0* @Date 2023/11/21 23:04*/
public class ArticleHtmlDocumentBuilder extends HtmlDocumentBuilder {@Overridepublic void buildHeader() {document.addHeader("Article Header");}@Overridepublic void buildBody() {document.addBody("Article Body");}@Overridepublic void buildFooter() {document.addFooter("Article Footer");}
}

指挥者(Director)类 - HtmlDirector:

public class HtmlDirector {/*** 构建者*/private HtmlDocumentBuilder builder;public HtmlDirector(HtmlDocumentBuilder builder) {this.builder = builder;}/*** 构建文档对象*/public void constructDocument() {builder.createNewHtmlDocument();builder.buildHeader();builder.buildBody();builder.buildFooter();}/*** 获取document对象** @return*/public HtmlDocument getDocument() {return builder.getDocument();}
}

现在测试使用创建者设计模式来构建一个HTML文档对象:

/*** 类描述:构建者设计模式的测试** @Author crysw* @Version 1.0* @Date 2023/11/21 23:16*/
@Slf4j
public class BuilderPatternTest {@Testpublic void testHtmlDocumentBuilder() {HtmlDocumentBuilder htmlDocumentBuilder = new ArticleHtmlDocumentBuilder();HtmlDirector director = new HtmlDirector(htmlDocumentBuilder);director.constructDocument();HtmlDocument htmlDocument = director.getDocument();log.info(">>> Constructed HTML Document: \n {}", htmlDocument.toString());}
}

在这个例子中,我们创建了一个表示HTML文档的产品类(HtmlDocument),一个抽象的创建者类(HtmlDocumentBuilder),一个具体的创建者类(ArticleHtmlDocumentBuilder)和一个指挥者类(HtmlDirector)。当我们需要创建一个新的HTML文档对象时,我们可以使用指挥者类来控制构建过程,从而实现了将构建过程与表示过程的分离。

以上是一个创建者设计模式的标准写法,为了创建一个对象,我们创建了很多辅助的类,总觉得不太合适。在工作中往往不会写的这么复杂,我们可以使用内部类来简化代码,以下是修改后的代码(甚至我们还移除了抽象层):

/*** 类描述:产品(Product),表示将要被构建的复杂对象。* 需求:创建一个复杂的HTML文档,它包含了标题、段落和图像等元素。我们可以使用创建者设计模式来构建HTML文档。** @Author crysw* @Version 1.0* @Date 2023/11/21 22:57*/
public class HtmlDocument {private String header;private String body;private String footer;public void addHeader(String header) {this.header = header;}public void addBody(String body) {this.body = body;}public void addFooter(String footer) {this.footer = footer;}@Overridepublic String toString() {return "<html><head>" + header + "</head><body>" + body + "</body><footer>"+ footer + "</footer></html>";}public static Builder builder() {return new Builder();}public static class Builder {protected HtmlDocument document;private Builder() {document = new HtmlDocument();}public Builder addHeader(String header) {document.addHeader(header);return this;}public Builder addBody(String body) {document.addBody(body);return this;}public Builder addFooter(String footer) {document.addFooter(footer);return this;}public HtmlDocument build() {return document;}}
}

再来测试创建一个HTML文档对象:

/*** 类描述:构建者设计模式的测试** @Author crysw* @Version 1.0* @Date 2023/11/21 23:16*/
@Slf4j
public class BuilderPatternTest {@Testpublic void testHtmlDocumentBuilder2() {HtmlDocument htmlDocument = HtmlDocument.builder().addHeader("This is the header").addBody("This is body").addFooter("This is footer").build();log.info(">>> Constructed HTML Document: \n {}", htmlDocument.toString());}
}

3.2 为什么需要建造者模式

(1) 根据复杂的配置项进行定制化构建。

先看一个mybaits中经典的案例,这个案例中使用了装饰器和创建者设计模式:

public class CacheBuilder {private final String id;private Class<? extends Cache> implementation;private final List<Class<? extends Cache>> decorators;private Integer size;private Long clearInterval;private boolean readWrite;private Properties properties;private boolean blocking;public CacheBuilder(String id) {this.id = id;this.decorators = new ArrayList<Class<? extends Cache>>();}public CacheBuilder implementation(Class<? extends Cache> implementation) {this.implementation = implementation;return this;}public CacheBuilder addDecorator(Class<? extends Cache> decorator) {if (decorator != null) {this.decorators.add(decorator);}return this;}public CacheBuilder size(Integer size) {this.size = size;return this;}public CacheBuilder clearInterval(Long clearInterval) {this.clearInterval = clearInterval;return this;}public CacheBuilder readWrite(boolean readWrite) {this.readWrite = readWrite;return this;}public CacheBuilder blocking(boolean blocking) {this.blocking = blocking;return this;}public CacheBuilder properties(Properties properties) {this.properties = properties;return this;}public Cache build() {setDefaultImplementations();Cache cache = newBaseCacheInstance(implementation, id);setCacheProperties(cache);// issue #352, do not apply decorators to custom caches// 根据配置的装饰器对原有缓存进行增强,如增加淘汰策略等if (PerpetualCache.class.equals(cache.getClass())) {for (Class<? extends Cache> decorator : decorators) {cache = newCacheDecoratorInstance(decorator, cache);setCacheProperties(cache);}cache = setStandardDecorators(cache);} else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {cache = new LoggingCache(cache);}return cache;}
}    

我们总结这个案例中的几个特点:

  • 参数有必填项id,有很多可选填的内容,通常必选项id通过构造器传入,可选项通过方法传递。
  • 真正的构建过程需要调用build方法,构建时需要根据已配置的成员变量的内容选择合适的装饰器,对目标cache进行增强。

(2) 实现不可变对象

创建者设计模式(Builder Design Pattern)可以实现不可变对象,即一旦创建完成,对象的状态就不能改变。这有助于保证对象的线程安全和数据完整性。下面是一个使用创建者设计模式实现的不可变对象的Java示例:

/*** 类描述:实现不可变对象** @Author crysw* @Version 1.0* @Date 2023/11/26 21:21*/
public final class ImmutablePerson {private final String name;private final int age;private final String address;private ImmutablePerson(Builder builder) {this.name = builder.name;this.age = builder.age;this.address = builder.address;}public String getName() {return name;}public int getAge() {return age;}public String getAddress() {return address;}public Builder builder() {return new Builder();}public static class Builder {private String name;private int age;private String address;public Builder() {}public Builder setName(String name) {this.name = name;return this;}public Builder setAge(int age) {this.age = age;return this;}public Builder setAddress(String address) {this.address = address;return this;}public ImmutablePerson build() {return new ImmutablePerson(this);}}
}

在这个例子中, ImmutablePerson 类具有三个属性: name 、age 和 address 。这些属性都是 final 修饰,一旦设置就不能更改。ImmutablePerson 的构造函数是私有的且没有set方法,外部无法直接创建该类的实例。需要使用内部的 Builder 类要创建一个 ImmutablePerson 实例,通过连续调用 Builder 类的方法,来为ImmutablePerson 设置属性。最后调用 build() 方法,创建一个具有指定属性的不可变 ImmutablePerson 实例。

3.3 源码应用

创建者设计模式在源码中有广泛的使用:
(1)jdk中的StringBuilder和StringBuffer,他们的实现不是完全按照标准的创建者设计模式设计,但也是一样的思想。

这两个类用于构建和修改字符串。它们实现了创建者模式,允许通过方法链来修改字符串。这些类在性能上优于 String 类,因为它们允许在同一个对象上执行多次修改,而不需要每次修改都创建一个新的对象。

StringBuilder builder = new StringBuilder();
builder.append("Hello").append(" ").append("World!");
String result = builder.toString();

(2)在ssm源码中使用创建者设计模式,如Spring中的BeanDefinitionBuilder 类,mybatis中SqlSessionFactoryBuilder 、
XMLConfigBuilder 、XMLMapperBuilder 、XMLStatementBuilder 、CacheBuilder 等。

(3)使用lombok实现创建者设计模式

Lombok 是一个 Java 库,它可以简化代码,提高开发效率,尤其是在实现模式和生成常用方法(例如 getter、setter、equals、hashCode 和 toString)时。以下是一个使用 Lombok 的创建者设计模式的例子:

首先在项目中引入了 Lombok 依赖:

<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version><scope>provided</scope>
</dependency>

创建一个类, 使用lombok的相关注解实现建造者模式:

/*** 类描述:使用Lombok构建实例,查看编译后的class文件,会自动生成构建对象的代码(使用的构建者模式)** @Author crysw* @Version 1.0* @Date 2023/11/20 23:19*/
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class Person {private String name;private Integer age;
}

反编译后的Person类:

package cn.itcast.designPatterns.builder;public class Person {private String name;private Integer age;public static PersonBuilder builder() {return new PersonBuilder();}public Person() {}public Person(String name, Integer age) {this.name = name;this.age = age;}public String getName() {return this.name;}public Integer getAge() {return this.age;}public void setName(String name) {this.name = name;}public void setAge(Integer age) {this.age = age;}public boolean equals(Object o) {if (o == this) {return true;} else if (!(o instanceof Person)) {return false;} else {Person other = (Person)o;if (!other.canEqual(this)) {return false;} else {Object this$name = this.getName();Object other$name = other.getName();if (this$name == null) {if (other$name != null) {return false;}} else if (!this$name.equals(other$name)) {return false;}Object this$age = this.getAge();Object other$age = other.getAge();if (this$age == null) {if (other$age != null) {return false;}} else if (!this$age.equals(other$age)) {return false;}return true;}}}protected boolean canEqual(Object other) {return other instanceof Person;}public int hashCode() {int PRIME = true;int result = 1;Object $name = this.getName();result = result * 59 + ($name == null ? 43 : $name.hashCode());Object $age = this.getAge();result = result * 59 + ($age == null ? 43 : $age.hashCode());return result;}public String toString() {return "Person(name=" + this.getName() + ", age=" + this.getAge() + ")";}// 内部类建造者public static class PersonBuilder {private String name;private Integer age;PersonBuilder() {}public PersonBuilder name(String name) {this.name = name;return this;}public PersonBuilder age(Integer age) {this.age = age;return this;}public Person build() {return new Person(this.name, this.age);}public String toString() {return "Person.PersonBuilder(name=" + this.name + ", age=" + this.age + ")";}}
}

测试lombok创建的建造者模式实现的类

@Test
public void testPerson() {Person person = builder().name("crysw").age(23).build();person.setName("panda");person.setAge(22);log.info(">>> person: {}", person);}

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

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

相关文章

SpringBoot进阶——解释springboot的自动配置原理

相关的博客文章如下&#xff1a; SpringBootApplication注解的理解——如何排除自动装配 & 分布式情况下如何自动加载 & nacos是怎么被发现的 引出 1.spring.factories文件存储能够进行自动配置的Bean信息&#xff1b; 2.EnableAutoConfiguration关闭数据源的自动配置…

Flask学习二:项目拆分、请求与响应、cookie

教程 教程地址&#xff1a; 千锋教育Flask2框架从入门到精通&#xff0c;Python全栈开发必备教程 老师讲的很好&#xff0c;可以看一下。 项目拆分 项目结构 在项目根目录下&#xff0c;创建一个App目录&#xff0c;这是项目下的一个应用&#xff0c;应该类似于后端的微服…

es6字符串模板之标签化模板

es6字符串模板 我们经常搞前端开发工作的都会用到。它可以保留字符串换行格式&#xff0c;还能接受变量。这个给前端的字符串拼接带来了非常大的方便。但是还有一种用法可能是我们平时还是没有怎么用到的。 styled-components 在项目中熟悉使用react的童鞋可能会用过styled-…

灯塔的安装

Docker 安装 docker 安装参考&#xff1a;https://docs.docker.com/engine/install/ shell脚本: curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh灯塔安装 mkdir docker-ARL;cd docker-ARL curl https://bootstrap.pypa.io/get-pip.py -o get-pip…

一些权限方面的思考

一些权限方面的思考 背景说明自定义注解解析自定义注解 背景 鉴权可以通过切面做抽取 说明 都是一些伪代码, 不能直接使用, 提供一种思路. 都是一些伪代码, 不能直接使用, 提供一种思路. 都是一些伪代码, 不能直接使用, 提供一种思路. 自定义注解 自定义注解: Permission …

【Linux学习】基础IO

目录 八.系统文件IO 8.1 前言 8.2 C语言文件IO C语言常用的基本函数 C语言默认打开的的三个流 8.3 系统文件IO open接口 close接口 write接口 read接口 8.4 C语言文件IO与系统文件IO的关系 八.系统文件IO 8.1 前言 系统文件 I/O&#xff08;输入/输出&#xff09;是指在…

pandas 如何获取dataframe的行的数量

pandas的dataframe提供了多种方法获取其中数据的行的数量&#xff0c;本偏文章就是介绍几种获取dataframe行和列出量的方法。 为了能够详细说明如何通过代码获取dataframe的行数和列数&#xff0c;需要先创建一个dataframe如下&#xff1a; import pandas as pdtechnologies …

C++设计模式之策略模式

策略模式 介绍示例示例测试运行结果应用场景优点总结 介绍 策略模式是一种行为设计模式。在策略模式中&#xff0c;可以创建一些独立的类来封装不同的算法&#xff0c;每一个类封装一个具体的算法&#xff0c;每一个封装算法的类叫做策略(Strategy)&#xff0c;为了保证这些策…

NX二次开发UF_CURVE_ask_wrap_curve_parents 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CURVE_ask_wrap_curve_parents Defined in: uf_curve.h int UF_CURVE_ask_wrap_curve_parents(tag_t curve_tag, tag_t * defining_face, tag_t * defining_plane, tag_t * defin…

18. Python 数据处理之 Numpy

目录 1. 简介2. 安装和导入Numpy3. ndarray 对象4. 基本运算5. 索引、切片和迭代6. 条件和布尔数组7. 变换形状8. 操作数组 1. 简介 数据分析的流程概括起来主要是&#xff1a;读写、处理计算、分析建模和可视化4个部分。 Numpy 是Python 进行科学计算&#xff0c;数据分析时…

C#每天复习一个重要小知识day10:静态类

1.静态类的概念 用static修饰的类。 静态类其实很简单&#xff0c;把它的特点和作用牢记就行了。 2.静态类的特点 1&#xff09;只能包含静态成员 //访问修饰符是啥自己定&#xff0c;但工具类里面的成员变量和成员方法一般都是用public的&#xff0c;除非你有某种需求&am…

关于银河麒麟操作系统黑屏问题

一.问题说明 安装xx所软件后在/etc/ld.so.conf.d/下创建了一个JinCEarth.conf的配置文件&#xff0c;ldconfig使其生效&#xff0c;然后安装任意一个包&#xff0c;重启时将会黑屏 1.根本原因&#xff1a;ld.so.cache的改变使得/usr/lib/x86_64-linux-gnu/libXrandr.so.2的库…

MySQL进阶_10.锁

文章目录 一、概述二、MySQL并发事务访问相同记录2.1、读-读2.2、写-写2.3、读-写2.4、并发问题的解决方案 三、锁的不同角度分类3.1、 读锁、写锁3.1.1、 锁定读 3.2、表级锁、页级锁、行锁3.2.1、表锁3.2.2、意向锁3.2.2.1、意向锁的作用3.2.2.2、意向锁的互斥性 3.2.3、自增…

2019年全国硕士研究生入学统一考试管理类专业学位联考英语(二)试题

文章目录 2019年考研英语二真题SectionⅠ Use of EnglishSection II Reading ComprehensionText 121——细节信息题22——细节信息题23——细节信息题24——细节信息题25——词义题 Text 226——细节信息题27——细节信息题28——细节信息题29——细节信息题30——态度题 Text …

springboot2.0 集成swagger3+Knife4j导出离线API 配置

springboot 版本2.3.1 一、集成swagger3 引入swagger依赖包 <!--swagger3集成--><dependency><groupId>org.springframework.plugin</groupId><artifactId>spring-plugin-core</artifactId><version>2.0.0.RELEASE</version>…

Linux面试题(三)

目录 34、du 和 df 的定义&#xff0c;以及区别&#xff1f; 35、awk 详解。 36、当你需要给命令绑定一个宏或者按键的时候&#xff0c;应该怎么做呢&#xff1f; 37、如果一个 linux 新手想要知道当前系统支持的所有命令的列表&#xff0c;他需要怎么做&#xff1f; 38、…

第一百八十一回 如何绘制阴影效果

文章目录 1. 概念介绍2. 使用方法2.1 SegmentedButton2.2 ButtonSegment 3. 代码与效果3.1 示例代码3.2 运行效果 4. 内容总结 1. 概念介绍 我们在本章回中介绍的SegmentedButton组件是一种分段式按钮&#xff0c;它把多个按钮连接成一组显示&#xff0c;组内再对不同的按钮进…

[计算机网络]应用层概述

0.写在前面: 该层为教学模型的最后一层,某种意义上来说是最接近各位开发者的一层,正因如此,这层中的很多定义和概念大家都有属于自己的理解, 完全按照书本反而才是异类,因此在这里我会去结合我做前端开发的一些经验,来处理和讲解一些概念,另外本层中的部分协议也不会过多阐述了…

VUE简易购物车程序

目录 效果预览图 完整代码 效果预览图 完整代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>…

Python 进阶(十一):高精度计算(decimal 模块)

《Python入门核心技术》专栏总目录・点这里 文章目录 1. 导入decimal模块2. 设置精度3. 创建Decimal对象4. 基本运算5. 比较运算6. 其他常用函数7. 注意事项8. 总结 大家好&#xff0c;我是水滴~~ 在进行数值计算时&#xff0c;浮点数的精度问题可能会导致结果的不准确性。为了…