设计模式-建造者模式

145 阅读7分钟

建造者模式 builderPattern

建造者模式可以将部件和其组装过程分开,一步一步创建一个复杂的对象。用户只需要指定复杂对象的类型就可以得到该对象,而无须知道其内部的具体构造细节。 比如一辆汽车的制造,用户不用关心汽车制造的过程,用户不需要关心汽车的方向盘,车轮,发动机这些部件是如何拼接出一辆汽车的,建造者模式就是将这些部件进行内部的组装然后返给用户一辆汽车即可

关于建造者模式主要包含以下几个角色:

  • 抽修建造者(Builder): 这个对象主要实现的复杂的对象是哪些部分创建的,并不涉及到具体部件的创建
  • 具体建造者 (ConcreteBuilder): 实现 Builder 的接口,完成复杂产品的各个部件的具体创建方法。在建造完成之后,提供一个方法,返回创建好的产品对象。
  • 产品类(Product): 需要创建的复杂的对象(包含多个组成部分)
  • 指挥类(Director):调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建(客户端一般只需要与指挥者进行交互)

image-20230518094808910

生成案例:自行车的生产

建造者模式-实现方式1

生产自行车是一个复杂的过程,它包含了车架,车座等组件的生产。而车架又有碳纤维,铝合金等材质的,车座有橡胶,真皮等材质。对于自行车的生产就可以使用建造者模式。这里Bike是产品,包含车架,车座等组件; Builder是抽象建造者, MobikeBuilder和HelloBuilder是具体的建造者;Director是指挥者。类图如下:

实现过程:

客户端调用其建造者 Director 进行建造, Director 通过 Builder 抽象建造者的多肽形式指向具体的 Builder 实现(碳纤维自行车/铝合金自行车)。 其中关于具体的实现过程(车架的选型,坐垫的选型,轮毂的选型。。。) Client 客户端与 Director 指挥者不用去关心,交于 Builder 抽象建造者的具体实现去完成

image-20230518110653431

建造者模式-实现方式2

除了以上这种,客户端不用关心具体的内部实现细节的建造者模式,还有一种关于建造者模式的实现。

当一个类的需要传入的参数很多的时候,如果创建该类的实例,代码的可读性就会变的非常的差,而且很容易引入错误,此时就可以利用建造者模式进行重构。

  1. 构造方法创建复杂对象的问题

构造方法如果参数过多,代码的可读性和易用性都会变差. 在使用构造函数时,很容易搞错参数的顺序,传递进去错误的参数值,导致很有隐蔽的BUG出现。

  1. set 方法出现的中间结果的无状态的问题

比如以下的代码:

比如下面的代码, 创建对象后使用set 的方式,那就会导致在第一个 set 之后,对象处于无效状态 Rectangle r = new Rectangle (); //无效状态 r.setWidth(2); //无效状态 r.setHeight(3); //有效状态

  1. set方法还破坏了"不可变对象"的密闭性

不可变对象: 对象创建好了,就不能再修改内部的属性值,下面的client类 就是典型的不可变对象,创建好的连接对象不能再改动

建造者方式实现

建造者使用步骤如下:

  1. 目标类的构造方法要传入Builder对象
  2. Builder建造者类位于目标类内部,并且使用static修饰
  3. Builder建造者对象提供内置的各种set方法,注意set方法返回的是builder对象本身
  4. Builder建造者类提供build()方法实现目标对象的创建
public class 目标类{
    //目标类的构造方法需要传入Builder对象
    public 目标类(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);
        }
    }
}
/**
 * @Author Peggy
 * @Date 2023-05-18 15:25
 * 建造者模式
 **/
public class RabbitMQClient {
    //私有构造方法
    private RabbitMQClient(Builder builder) {}
​
    @Data
    @Accessors(chain = true)
    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;
        int connectionTimeout = 1000;
​
        //返回建造好的复杂对象
        public RabbitMQClient build() {
            //首先进行校验
            if (mode == 1) { //工作队列模式不需要设计交换机,但队列名一定要有
                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) { //路由模式必须设计交换机,但是不能设计队列
                if (exchange == null) {
                    throw new RuntimeException("路由模式下必须设置交换机");
                }
                if (queue != null) {
                    throw new RuntimeException("路由模式无须设计队列名称");
                }
            }
            return new RabbitMQClient(this);
        }
    }
    public void sendMessage(String msg) {
        System.out.println("发送消息......");
    }
}

所以总的来说,建造者模式其实是解决了两方面的主要问题

  1. 由于如果对外不开放其 set 方法,在通过有构造创建对象的时候,参数过多就可能会出现参数的混乱
  2. 当对外开放其 set 方法就有可能出现一些对象当前的空状态无效的情况,无法在进行传参的时候进行拦截

但是如果使用期建造者模式-将其 set 方法封装在静态内部类当中,内部类的 build 方法其传入的状态值进行校验,返回其目标对象的创建。

建造者模式于工厂模式之间的区别

前的关于建造者模式的两个案例感觉于工厂模式好像其大同小异的,但其实他们具体的区别在于实现上的细度不同。

工厂模式-抽奖案例 :

  • 用户只需要调用其按照不同的种类进行抽奖,返回其对应的奖品(优惠价/实体奖品/奖金)

建造者模式-自行车案例:

  • 用户需要调用其生产不同的自行车(碳纤维/铝合金)

建造者模式-目标对象案例:

  • 用户通过传入不同参数获取具体的目标对象

两者对比:

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

举例:

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

应用场景:

  • 建造者(Builder)模式创建的是复杂对象,其产品的各个部分经常面临着剧烈的变化,但将它们组合在一起的算法却相对稳定,所以它通常在以下场合使用。

    • 创建的对象较复杂,由多个部件构成,各部件面临着复杂的变化,但构件间的建造顺序是稳定的。
    • 创建复杂对象的算法独立于该对象的组成部分以及它们的装配方式,即产品的构建过程和最终的表示是独立的。