最后
笔者已经把面试题和答案整理成了面试专题文档
以上问题的解决就是构建者模式的应用场景。构建者模式,将复杂对象的创建过程与其表示分离,通过设置不同的可选参数,“定制化”创建不同的对象表示。
构建者模式是如何解决问题的呢?其核心思想就是:
-
将复杂对象的创建过程(比如set属性值),委托给构建者去做。
-
最后通过构建者的构建方法,真正去创建对象。
-
构建方法里调用需要实例化的类的构造函数,并将构建者的属性值复制给该对象。复制前可以做一些参数校验和默认值赋值等操作。
1、通用写法
类图很简单,代码也很简单。
public class Product {
private String s1;
private String s2;
private String s3;
private Integer i1;
private Integer i2;
private Integer i3;
private Product(Builder builder) {
this.s1 = builder.s1;
this.s2 = builder.s2;
this.s3 = builder.s3;
this.i1 = builder.i1;
this.i2 = builder.i2;
this.i3 = builder.i3;
}
// 省略getter 不提供setter
@Override
public String toString() {
return “Product{” +
“s1='” + s1 + ‘’’ +
“, s2='” + s2 + ‘’’ +
“, s3='” + s3 + ‘’’ +
“, i1=” + i1 +
“, i2=” + i2 +
“, i3=” + i3 +
‘}’;
}
public static class Builder {
private String s1;
private String s2;
private String s3;
private Integer i1;
private Integer i2;
private Integer i3;
public static Builder create(String s1) {
return new Builder(s1);
}
private Builder(String s1) {
this.s1 = s1;
}
public Builder setS1(String s1) {
this.s1 = s1;
return this;
}
public Builder setS2(String s2) {
this.s2 = s2;
return this;
}
public Builder setS3(String s3) {
this.s3 = s3;
return this;
}
public Builder setI1(Integer i1) {
this.i1 = i1;
return this;
}
public Builder setI2(Integer i2) {
this.i2 = i2;
return this;
}
public Builder setI3(Integer i3) {
this.i3 = i3;
return this;
}
public Product build() {
// 1. s1 必传
if (s1 == null || s2 == “”) {
throw new IllegalArgumentException(“s1必传,不可为空!”);
}
// 2. s2 s3 非必填,但是具有依赖关系,即s2传了,s3也必须传
if (s2 != null && s2 != “”) {
if (s3 == null || s3 == “”) {
throw new IllegalArgumentException(“s2与s3具有依赖关系!”);
}
}
// 3. i1 不传或者 i1 <= 0 则默认为 1
if (i1 == null || i1 <= 0) {
i1 = 1;
}
// 4. i2非必传 但是需要校验有效性,不能小于0
if (i2 != null && i2 < 0) {
throw new IllegalArgumentException(“i2 不能小于0”);
}
Product product = new Product(this);
return product;
}
}
}
public class Test {
public static void main(String[] args) {
Product product = Product.Builder.create(“1”)
.setS2(“2”)
.setS3(“3”)
.setI1(0)
.setI2(2)
.setI3(3)
.build();
System.out.println(product.toString());
}
}
// Product{s1=‘1’, s2=‘2’, s3=‘3’, i1=1, i2=2, i3=3}
注意:
-
Builder
可以是内部类,也可以是外部类,视情况而定,一般情况是作为产品类的内部类,这样产品类多了,不至于多出很多Builder
类,让代码结构变得复杂。 -
Builder
和产品类中代码重复,产品类的属性需要复制一份到Builder
,并且要保持同步更新。 -
Builder
中set
方法需要返回Builder
本身,达到链式调用的效果。 -
Builder
的build()
方法中实例化产品类,并且做一些必要的参数校验,非必填的参数校验也可以放到set
里。 -
Product
产品类的构造函数设置成private
的,并且没有对外提供set
方法,主要是为了创建产品类只能通过Builder
,并且创建出来的对象是不可修改的。 -
Product
的构造函数的参数为Builder
,这样为了方便将值复制给Product
,也可以不用这样做。 -
Product.Builder
对外提供了一个静态方法create
,这个方法可有可无,是将Builder
的创建隔离,同时一些必填参数也可以通过Builder
的构造函数传入。
2、构建者模式的优缺点
(1)优点:隔离了复杂对象的创建细节,方便客户端调用,减少了不必要的bug。
(2)缺点:代码重复,有一定的维护成本。
总结一句就是,方便他人,麻烦自己。
3、构建者模式与工厂模式的区别
(1)工厂模式是用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创建哪种类型的对象,一般这个创建过程也比较简单,如果复杂的话,可以工厂模式和构建者模式结合。
(2)构建者模式是用来创建一种类型的复杂对象,通过设置不同的可选参数,“定制化”创建不同的对象。
1、JDK的StringBuilder
JDK中StringBuilder
其实就是用了构建者模式,append
拼接字符串,最后toString
生成一个String
对象。
总结
大型分布式系统犹如一个生命,系统中各个服务犹如骨骼,其中的数据犹如血液,而Kafka犹如经络,串联整个系统。这份Kafka源码笔记通过大量的设计图展示、代码分析、示例分享,把Kafka的实现脉络展示在读者面前,帮助读者更好地研读Kafka代码。
麻烦帮忙转发一下这篇文章+关注我
大型分布式系统犹如一个生命,系统中各个服务犹如骨骼,其中的数据犹如血液,而Kafka犹如经络,串联整个系统。这份Kafka源码笔记通过大量的设计图展示、代码分析、示例分享,把Kafka的实现脉络展示在读者面前,帮助读者更好地研读Kafka代码。
麻烦帮忙转发一下这篇文章+关注我
[外链图片转存中…(img-x9AMHgvK-1715815327067)]