1.基本介绍
- 建造者模式(Builder Pattern) 又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。
- 建造者模式 是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。
2.建造者模式基本介绍
- Product(产品角色): 一个具体的产品对象。
- Builder(抽象建造者): 创建一个 Product 对象的各个部件指定的 接口/抽象类。
- ConcreteBuilder(具体建造者): 实现接口,构建和装配各个部件。
- Director(指挥者): 构建一个使用 Builder 接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。
3.不⽤建造者有什么麻烦?
假设我们要⾃⼰开发⼀个RabbitMQ消息队列的客户端,有很多需要初始化的参数,你会怎么做?
1、基于构造⽅法为属性赋值⽆法适⽤于灵活多变的环境,且参数太⻓很难使⽤
public class RabbitMQClientSample1 { private String host = "127.0.0.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("路由模式⽆须设置队列名称"); } } //其他各种验证 } 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"); } }
2、使用set方法灵活赋值
public class RabbitMQClientSample2 { private String host = "127.0.0.1"; private int port = 5672; private int mode; private String exchange; private String queue; private boolean isDurable = true; int connectionTimeout = 20; //让对象不可变 private RabbitMQClientSample2(){ } 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.setQueue("queue"); client.validate(); client.sendMessage("Test"); } }
利⽤SET⽅法虽然灵活,但是存在中间状态,且属性校验时有前后顺序约束,或者还需要构建额外的校验⽅法。 并且SET⽅法破坏了“不可变对象”的密闭性
如何保障灵活组织参数,有可以保证不会存在中间状态以及基本信息不会对外泄露呢?
4.建造者模式是⼀个好选择
构建者模式的特点:摆脱超⻓构造⽅法参数的束缚的同时也保护了”不 可变对象“的密闭性
建造者模式的格式如下:
- ⽬标类的构造⽅法要求传⼊Builder对象
- Builder建造者类位于⽬标类内部且⽤static描述
- Builder建造者对象提供内置属性与各种set⽅法,注意set⽅法返回 Builder对象本身
- Builder建造者类提供build()⽅法实现⽬标类对象的创建 Builder
public class ⽬标类(){ //⽬标类的构造⽅法要求传⼊Builder对象 public ⽬标类(Builder builder){ } public 返回值 业务⽅法(参数列表){ //doSth } //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); } } }
建造者模式进行优化
public class RabbitMQClient { private RabbitMQClient(Builder builder){ } public void sendMessage(String msg) { System.out.println("正在发送消息:" + msg); } public static class Builder { private String host = "127.0.0.1"; private int port = 5672; private int mode; private String exchange; private String queue; private boolean isDurable = true; private int connectionTimeout = 20; public Builder setHost(String host) { this.host = host; return this; } public Builder setPort(int port) { this.port = port; return this; } public Builder setMode(int mode) { this.mode = mode; return this; } public Builder setExchange(String exchange) { this.exchange = exchange; return this; } public Builder setQueue(String queue) { this.queue = queue; return this; } public Builder setDurable(boolean durable) { isDurable = durable; return this; } public RabbitMQClient build() { 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 new RabbitMQClient(this); } } public static void main(String[] args) { RabbitMQClient client = new RabbitMQClient.Builder().setHost("192.168.31.201").setMode(2).setExchange("test-exchange").build(); client.sendMessage("Test"); } }
5.与工厂模式有何区别?
1、建造者模式是让建造者类来负责对象的创建工作
2、工厂模式,是由工厂类来负责对象创建的工作
3、实际上,工厂模式是用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创建哪种类型的对象。建造者模式是用来创建一种类型的复杂对象,通过设置不同的可选参数,“定制化”地创建不同的对象。
顾客走进一家餐馆点餐,我们利用工厂模式,根据用户不同的选择,来制作不同的食物,比如披萨、汉堡、沙拉。对于披萨来说,用户又有各种配料可以定制,比如奶酪、西红柿、起司,我们通过建造者模式根据用户选择的不同配料来制作披萨。
转载请注明:西门飞冰的博客 » 设计模式—建造者模式