个人主页:金鳞踏雨
个人简介:大家好,我是金鳞,一个初出茅庐的Java小白
目前状况:22届普通本科毕业生,几经波折了,现在任职于一家国内大型知名日化公司,从事Java开发工作
我的博客:这里是CSDN,是我学习技术,总结知识的地方。希望和各位大佬交流,共同进步 ~
比较简单,但是很经常用!
个人感悟:
为什么会有这种设计模式?往往是因为语言或者框架本身的缺陷而导致的。这个语言或者框架本身就不支持这种开发形式,我们必须使用一种开发"套路"来解决这个问题!
那为什么有一些设计模式我们在开发中很少见呢?因为本身一些框架就提供了这些功能!我们使用起来就很简单,所以就没有那么重要了!
Builder 模式,中文翻译为建造者模式或者构建者模式,也有人叫它生成器模式。
实际上,建造者模式的原理和代码实现非常简单,掌握起来并不难,难点在于应用场景。比如,你有没有考虑过这样几个问题:直接使用构造函数或者配合 set 方法就能创建对象,为什么还需要建造者模式来创建呢?建造者模式和工厂模式都可以创建对象,那它们两个的区别在哪里呢,话不多说,我们直接来学习:
我们考虑一个文档编辑器的例子。假设我们需要创建一个复杂的HTML文档,它包含了标题、段落和图像等元素。
1、产品(Product)类 - HTML文档(HtmlDocument):
- 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;
- }
-
- @Override
- public String toString() {
- return "" + header + "" + body + " + footer + "";
- }
- }
2、抽象创建者(Abstract Builder)类 - HtmlDocumentBuilder:
- public abstract class HtmlDocumentBuilder {
- protected HtmlDocument document;
-
- public HtmlDocument getDocument() {
- return document;
- }
-
- public void createNewHtmlDocument() {
- document = new HtmlDocument();
- }
-
- public abstract void buildHeader();
- public abstract void buildBody();
- public abstract void buildFooter();
- }
3、具体创建者(Concrete Builder)类 - ArticleHtmlDocumentBuilder:
- // 实现抽象
- public class ArticleHtmlDocumentBuilder extends HtmlDocumentBuilder {
- @Override
- public void buildHeader() {
- document.addHeader("Article Header");
- }
-
- @Override
- public void buildBody() {
- document.addBody("Article Body");
- }
-
- @Override
- public void buildFooter() {
- document.addFooter("Article Footer");
- }
- }
4、指挥者(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();
- }
-
- public HtmlDocument getDocument() {
- return builder.getDocument();
- }
- }
现在我们可以使用创建者设计模式来构建一个HTML文档对象:
- public class Main {
- public static void main(String[] args) {
- HtmlDocumentBuilder articleBuilder = new ArticleHtmlDocumentBuilder();
- HtmlDirector director = new HtmlDirector(articleBuilder);
-
- director.constructDocument();
- HtmlDocument document = director.getDocument();
-
- System.out.println("Constructed HTML Document: \n" + document);
- }
- }
在这个例子中,我们创建了一个表示HTML文档的产品类(HtmlDocument),一个抽象的创建者类(HtmlDocumentBuilder),一个具体的创建者类(ArticleHtmlDocumentBuilder)和一个指挥者类(HtmlDirector)。当我们需要创建一个新的HTML文档对象时,我们可以使用指挥者类来控制构建过程,从而实现了将构建过程与表示过程的分离。
以上是一个创建者设计模式的标准写法,事实,我们在工作中往往不会写的这么复杂,为了创建一个对象,我们创建了很多辅助的类,总觉得不太合适,在这个案例中,我们可以使用内部类来简化代码,以下是修改后的代码(甚至我们还移除了抽象层)
- 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;
- }
-
- @Override
- public String toString() {
- return "" + header + "" + body + " + footer + "";
- }
-
- public static class Builder {
- protected HtmlDocument document;
-
- public 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文档对象
- public class Main {
- public static void main(String[] args) {
- HtmlDocument.Builder builder = new HtmlDocument.Builder();
- HtmlDocument document = builder.addHeader("This is the header")
- .addBody("This is the body")
- .addFooter("This is the footer")
- .build();
-
- System.out.println("Constructed HTML Document: \n" + document);
- }
- }
将创建者类(Builder)作为HTML文档类(HtmlDocument)的内部类。
这样做可以让代码更加紧凑。此外,我们使用了一种流式接口,使得在客户端代码中创建HTML文档对象更加简洁。
首先,我们先看一个mybaits中经典的案例,这个案例中使用了装饰器和创建者设计模式:
- public class CacheBuilder {
- private final String id;
- private Class extends Cache> implementation;
- private final List
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<>();
- }
-
- public CacheBuilder size(Integer size) {
- this.size = size;
- return this;
- }
-
- public CacheBuilder clearInterval(Long clearInterval) {
- this.clearInterval = clearInterval;
- 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() {
- // 使用了装饰器模式,添加 LruCache
- setDefaultImplementations();
- Cache cache = newBaseCacheInstance(implementation, id);
- setCacheProperties(cache);
- // 根据配置的装饰器对原有缓存进行增强,如增加淘汰策略等
- 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;
- }
- }
我们总结这个案例中的几个特点:
1、参数有必填项id,有很多可选填的内容;通常必选项id通过构造器传入,可选项通过方法传递。
2、真正的构建过程需要调用build()方法,构建时需要根据已配置的成员变量的内容选择合适的装饰器,对目标cache进行增强。
创建者设计模式(Builder Design Pattern)可以实现不可变对象,即一旦创建完成,对象的状态就不能改变。这有助于保证对象的线程安全和数据完整性。
- 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 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 的构造函数是私有的,外部无法直接创建该类的实例。要创建一个 ImmutablePerson 实例,需要使用内部的 Builder 类。通过连续调用 Builder 类的方法,我们可以为 ImmutablePerson 设置属性。最后,通过调用 build() 方法,我们创建一个具有指定属性的不可变 ImmutablePerson 实例。
实际上,使用建造者模式创建对象,还能避免对象存在无效状态。我再举个例子解释一下。比如我们定义了一个长方形类,如果不使用建造者模式,采用先创建后 set 的方式,那就会导致在第一个 set 之后,对象处于无效状态。
- Rectangle r = new Rectange(); // r is invalid
- r.setWidth(2); // r is invalid
- r.setHeight(3); // r is valid
为了避免这种无效状态的存在,我们就需要使用构造函数一次性初始化好所有的成员变量。如果构造函数参数过多,我们就需要考虑使用建造者模式,先设置建造者的变量,然后再一次性地创建对象,让对象一直处于有效状态。
实际上,如果我们并不是很关心对象是否有短暂的无效状态,也不是太在意对象是否是可变的。比如,对象只是用来映射数据库读出来的数据,那我们直接暴露 set() 方法来设置类的成员变量值是完全没问题的。而且,使用建造者模式来构建对象,代码实际上是有点重复的。
建造者模式是用来创建一种类型的复杂对象,通过设置不同的可选参数,“定制化”地创建不同的对象。
我们把类的必填属性放到构造函数中,强制创建对象的时候就设置。
实际上,其实不用非得把工厂模式、建造者模式分得那么清楚,我们需要知道的是,每个模式为什么这么设计,能解决什么问题。只有了解了这些最本质的东西,我们才能不生搬硬套,才能灵活应用,甚至可以混用各种模式创造出新的模式,来解决特定场景的问题。
创建者设计模式在源码中有广泛的使用常见:
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 简单地实现创建者设计模式,可以使用 @Builder 注解。自动生成创建者类和相关方法。
首先,引入 Lombok 库。
- <dependencies>
- <dependency>
- <groupId>org.projectlombokgroupId>
- <artifactId>lombokartifactId>
- <version>1.18.20version>
- <scope>providedscope>
- dependency>
- dependencies>
然后,创建一个使用 Lombok 的创建者设计模式的类:
- @Getter
- @ToString
- @Builder
- public class Person {
- private String name;
- private int age;
- private String address;
- }
在上面的示例中,我们使用了 @Builder 注解来自动生成创建者类和相关方法。此外,我们还使用了 @Getter 注解来自动生成 getter 方法,以及 @ToString 注解来自动生成 toString 方法。
自动生成的创建者类创建 Person 对象
- Person person = Person.builder()
- .name("John Doe")
- .age(30)
- .address("123 Main St")
- .build();
通过 Lombok,可以轻松地实现创建者设计模式,减少样板代码,提高代码可读性。
文章到这里就结束了,如果有什么疑问的地方,可以在评论区指出~
希望能和大佬们一起努力,诸君顶峰相见
再次感谢各位小伙伴儿们的支持!!!