建造者模式
跳到导航
跳到搜索
https://www.bilibili.com/video/BV1rV4y1s7JG
1.不用建造者有什么麻烦?
假设我们要自己开发一个RabbitMQ消息队列的客户端,有很多需要初始化的参数,你会怎么做?
package io.github.jihch;
public class RabbitMQClientSample1 {
private String host = "127.9.9.1";
private int port = 5672;
private int mode;
private String exchange;
private String queue;
private boolean isDurable = true;
int connectionTimeout = 1000;
private RabbitMQClientSample1(String host, int port, int mode, String exchange, String queue, boolean isDurable,
int connectionTimeout) {
this.host = host;
this.port = port;
this.mode = mode;
this.exchange = exchange;
this.queue = queue;
this.isDurable = isDurable;
this.connectionTimeout = connectionTimeout;
if (mode == 1) {//工作队列模式不需要设置交换机,但queue必填
if (exchange != null) {
throw new RuntimeException("工作队列模式无须设计交换机");
}
if (queue == null || queue.trim().equals("")) {
throw new RuntimeException("工作队列模式必须设置队列名称");
}
if (isDurable == false) {
throw new RuntimeException("工作队列模式必须开启数据持久化");
}
} else if (mode == 2) { //路由模式必须设置交换机,但不能设置 queue 队列
if (exchange == null || exchange.trim().equals("")) {
throw new RuntimeException("路由模式请设置交换机");
}
if (queue != null) {
throw new RuntimeException("路由模式无需设置队列名称");
}
}
//其他各种验证
}// end constructor
public void sendMessage(String msg) {
System.out.println("正在发送消息:" + msg);
}
public static void main(String[] args) {
RabbitMQClientSample1 client = new RabbitMQClientSample1("192.168.31.210", 5672, 2, "sample-exchange", null,
true, 5000);
client.sendMessage("Test");
}
}
每次使用构造方法创建新的对象都要传入很多参数(想想生产环境如果按数据表抽象,一张表有多少字段?!就算默认值可以为 null,一个个字段对照填写费劲不费劲?!),其中有一些参数在一些情况下使用默认值就可以,并不是必要填写的,还是改用 set 方法灵活赋值吧
package io.github.jihch;
public class RabbitMQClientSample2 {
private String host = "127.9.9.1";
private int port = 5672;
private int mode;
private String exchange;
private String queue;
private boolean isDurable = true;
int connectionTimeout = 1000;
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public int getMode() {
return mode;
}
public void setMode(int mode) {
this.mode = mode;
}
public String getExchange() {
return exchange;
}
public void setExchange(String exchange) {
if (mode == 1) {//工作队列模式不需要设置交换机,但queue必填
if (exchange != null) {
throw new RuntimeException("工作队列模式无须设计交换机");
}
if (queue == null || queue.trim().equals("")) {
throw new RuntimeException("工作队列模式必须设置队列名称");
}
if (isDurable == false) {
throw new RuntimeException("工作队列模式必须开启数据持久化");
}
} else if (mode == 2) { //路由模式必须设置交换机,但不能设置 queue 队列
if (exchange == null || exchange.trim().equals("")) {
throw new RuntimeException("路由模式请设置交换机");
}
if (queue != null) {
throw new RuntimeException("路由模式无需设置队列名称");
}
}
this.exchange = exchange;
}
public String getQueue() {
return queue;
}
public void setQueue(String queue) {
if (mode == 1) {//工作队列模式不需要设置交换机,但queue必填
if (exchange != null) {
throw new RuntimeException("工作队列模式无须设计交换机");
}
if (queue == null || queue.trim().equals("")) {
throw new RuntimeException("工作队列模式必须设置队列名称");
}
if (isDurable == false) {
throw new RuntimeException("工作队列模式必须开启数据持久化");
}
} else if (mode == 2) { //路由模式必须设置交换机,但不能设置 queue 队列
if (exchange == null || exchange.trim().equals("")) {
throw new RuntimeException("路由模式请设置交换机");
}
if (queue != null) {
throw new RuntimeException("路由模式无需设置队列名称");
}
}
this.queue = queue;
}
public boolean isDurable() {
return isDurable;
}
public void setDurable(boolean durable) {
isDurable = durable;
}
public int getConnectionTimeout() {
return connectionTimeout;
}
public void setConnectionTimeout(int connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
//没办法,必须增加一个额外的 validate 方法验证对象是否符合要求
public boolean validate() {
if (mode == 1) {//工作队列模式不需要设置交换机,但queue必填
if (exchange != null) {
throw new RuntimeException("工作队列模式无须设计交换机");
}
if (queue == null || queue.trim().equals("")) {
throw new RuntimeException("工作队列模式必须设置队列名称");
}
if (isDurable == false) {
throw new RuntimeException("工作队列模式必须开启数据持久化");
}
} else if (mode == 2) { //路由模式必须设置交换机,但不能设置 queue 队列
if (exchange == null || exchange.trim().equals("")) {
throw new RuntimeException("路由模式请设置交换机");
}
if (queue != null) {
throw new RuntimeException("路由模式无需设置队列名称");
}
}
return true;
}
public void sendMessage(String msg) {
System.out.println("正在发送消息:" + msg);
}
public static void main(String[] args) {
RabbitMQClientSample2 client = new RabbitMQClientSample2();
client.setHost("192.168.31.210");
client.setMode(1);
client.setDurable(true);
client.validate();
client.sendMessage("Test");
}
}
利用 set 方法虽然灵活,但是存在中间状态,且属性校验时有前后顺序约束,或者还需要构建额外的校验方法
并且 set 方法破坏了“不可变对象”的密闭性
怎么才能既可以灵活组织参数,又保证不会存在中间状态,还能保证基本信息不会对外泄漏呢?
建造者模式是一个好选择
建造者模式的格式如下:
- 目标类的构造方法要求传入 Builder 对象
- Builder 建造者类位于目标类内部且用 static 修饰
- Builder 建造者对象提供内置属性与各种 set 方法,注意 set 方法返回 Builder 对象本身
- Builder 建造者提供 build() 方法实现目标类对象的创建
Builder
package io.github.jihch;
public class 目标类 {
//目标类的构造方法要求传入 Builder 对象
private 目标类(Builder builder) {
}
public 返回值 业务方法(参数列表) {
}
//Builder 建造者类位于目标类内部且用 static 描述
public static class Builder {
//Builder 建造者对象提供内置属性与各种 set 方法,注意 set 方法返回 Builder 对象本身
private String xxx;
public Builder setXxx(String xxx) {
this.xxx = xxx;
return this;
}
//Builder 建造者类提供 build() 方法实现目标类对象的创建
public 目标类 build() {
//业务校验
return new 目标类(this);
}
}
}