由于blog各种垃圾评论太多,而且本人审核评论周期较长,所以懒得管理评论了,就把评论功能关闭,有问题可以直接qq骚扰我

设计模式—建造者模式

JAVA 西门飞冰 3746℃
[隐藏]

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、实际上,工厂模式是用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创建哪种类型的对象。建造者模式是用来创建一种类型的复杂对象,通过设置不同的可选参数,“定制化”地创建不同的对象。

网上有一个经典的例子很好地解释了两者的区别:

顾客走进一家餐馆点餐,我们利用工厂模式,根据用户不同的选择,来制作不同的食物,比如披萨、汉堡、沙拉。对于披萨来说,用户又有各种配料可以定制,比如奶酪、西红柿、起司,我们通过建造者模式根据用户选择的不同配料来制作披萨。

转载请注明:西门飞冰的博客 » 设计模式—建造者模式

喜欢 (0)or分享 (0)