Builder建造者模式
适合的场景
对象的装配比较复杂时,一般来说是指对象本身字段比较多,有些字段必需要指定,有些字段则是可选的。在Grpc中使用的非常多,Grpc方法入参采用builder模式,通过protobuf文件生成这类代码生成也变得方便,而不用生成各种各样的构造器。
优势
- 减少对象使用者的负担,将对象本身和对象的构建分离开来,不直接使用对象的构造器进行创建。
- 对象本身可不提供setter方法,让对象是一个不可变类,在多线程环境下也更安全。
- 提供流式Api,让代码书写更加方便。
关键点
- 对象本身不对外暴露构造器,而是通过静态内部类Builder来实现。提供静态方法将Builder暴露给外部。
- Builer类中必须参数用构造器来填充,非必选参数用单独的api来设置,并返回Builer对象本身
- Builder在build方法进行对象的生成,一般直接调用私有的构造函数。
Demo
下面是服务发现场景中对一个服务实例对象的构造,话不多说,上代码。
import java.util.Objects;
import java.util.UUID;
/**
* buildr模式
* @author puyujie
* @version 1.0.0
* @since 2022/11/05 21:59
*/
public class ServerInstance {
// 必填参数
private final String name;
// 必填参数
private final String address;
// 可选参数
private final String id;
// 可选参数
private final Integer port;
// 可选参数
private final Integer sslPort;
public static Builder builder(String name, Integer port) {
return new Builder(name, port);
}
// 私有构造函数,外部不允许访问
private ServerInstance(String name, String id, String address, Integer port, Integer sslPort) {
// id为null,给一个默认值
this.id = Objects.isNull(id) ? UUID.randomUUID().toString() : id;
this.name = name;
this.address = address;
this.port = port;
this.sslPort = sslPort;
}
@Override
public String toString() {
return "ServerInstance{" +
"name='" + name + ''' +
", address='" + address + ''' +
", id='" + id + ''' +
", port=" + port +
", sslPort=" + sslPort +
'}';
}
private static class Builder {
// 必填参数
private String name;
// 必选参数
private Integer port;
// 可选参数
private String id;
// 可选参数
private Integer sslPort;
// 必填参数
private String address;
// 必须参数用构造器设置
public Builder(String name, Integer port) {
this.name = name;
this.port = port;
}
// 非必选参数用单独的api进行设置
public Builder id(String id) {
this.id = id;
return this;
}
// 非必选参数用单独的api进行设置
public Builder address(String address) {
this.address = address;
return this;
}
// 非必选参数用set方法构造
public Builder sslPort(Integer port) {
this.sslPort = port;
return this;
}
public ServerInstance build() {
return new ServerInstance(name, id, address, port, sslPort);
}
}
public static void main(String[] args) {
System.out.println(ServerInstance.builder("service", 20011).sslPort(200).build());
}
}