21 设计模式:构造者模式(Builder模式,建造者模式)--创建型

86 阅读8分钟

代码太烂被女朋友抛弃的不知道多少天,她已经有了新欢,而你一无所有,包括代码!

最近工作越来越难了,老板的PUA,外加上工作环境,真的很烂,当你觉得周围环境变差的时候,是时候抽身反思了,闲暇之余还是要学习点知识,多一点自保能力和选择权。

创建型模式之:

  • 单例模式
  • 工厂模式
  • 构造者模式
  • 克隆模式

1.什么是构造者模式?

简单的讲,构造者模式和工厂模式一样,也是生产对象的一种设计模式,那么已经有了工厂模式,为什么还有构造者模式呢?

1.1构造者模式的特性(直接上例子)

创建一个汽车,不使用构造者模式,是怎么实现的呢?

//创建一个汽车类,并且定义它的各种属性
public class Car {
    private String engine;
    private String color;
    private int seats;
    private float length

    public Car(String engine, String color, int seats, float length) {
        this.engine = engine;
        this.color = color;
        this.seats = seats;
        this.length = length;
    }

    // 省略 getter 和 setter 方法
}

//使用
Car car = new Car("TESEL", black, 4);

这个方法可以使用,但是会存在什么问题呢? 1.seats是四个目前是默认的,需要传入吗,并且Color不想传入可以吗? 2. 每次新创建一个对象都要传入很多参数,并且存在传错参数的风险,在设计时,应该考虑到使用带来的风险。

怎么解决这个问题呢?

那当然是为用户提供便捷可选择性,构造函数只保留车的类型,其他的座位,颜色,用户可以通过set()方法动态设置,不设置就使用默认值。

//创建一个汽车类,并且定义它的各种属性
public class Car {
    private String engine;
    private String color;
    private int seats;
    private float length

    public Car(String engine) {
        this.engine = engine;
        this.color = “black”;
        this.seats = 4;
        this.length = 4;
    }

    // 省略 getter 和 setter 方法
}

//使用
Car car = new Car("TESEL");
car.setColor("white");

但是这样还是没有使用构造者模式,这样会有什么问题呢?假如使用者对业务不熟悉,他只调用了setLength()方法,给车的长度设置了2.2米,那这样seats为4肯定是不满足的,我们在设计时,人为的给用户提供了很多产生bug 的机会,假如我们在每次set一次就加一个校验,那就使得代码十分的冗余.

(想象一下,产品给你出了一个需求,你刚刚开发完,她就变更了需求,这样你前面考虑的场景在新的条件下可能就不适用了,是不是很生气?那么怎么做才能避免被这些低级的人搞得心情烦躁呢?)

肯定是在需求开始开发时就需求评审,让产品把她想要的效果一次性都给你,然后看需不需要补充和修改那么,你就能理解这个解决思路,你就能理解构造者模式的设计思想了!

//创建一个汽车类,并且定义它的各种属性
public class Car {
    private String engine;
    private String color;
    private int seats;
    private float length


    private Car(CarBuilder builder) {
        this.engine = builder.engine;
        this.color = builder.color;
        this.seats = builder.seats;
        this.length = builder.length;
    }

    // 省略 getter 和 setter 方法


      //创建一个构造者类,用于构建汽车对象
    public class CarBuilder {
        private String engine;
        private String color;
        private int seats;
        private float length


        public CarBuilder() {
            // 设置一些默认值
            this.engine = "V6";
            this.color = "Black";
            this.seats = 4;
            this.length = 3.0;

        }

        public CarBuilder setEngine(String engine) {
            this.engine = engine;
            return this;
        }

        public CarBuilder setColor(String color) {
            this.color = color;
            return this;
        }

        public CarBuilder setSeats(int seats) {
            this.seats = seats;
            return this;
        }

        //所有参数设置完了后,在build内进行校验。
        public Car build() {
            if(length<3.0 && seats == 4){
                throw new IllegalArgumentException("...");
            }
            return new Car(this);
        }
    }
}

//使用
public class Main {
    public static void main(String[] args) {
        Car car = new CarBuilder()
                        .setEngine("V8")
                        .setColor("Red")
                        .setSeats(2)
                        .build();

        System.out.println("Car details:");
        System.out.println("Engine: " + car.getEngine());
        System.out.println("Color: " + car.getColor());
        System.out.println("Seats: " + car.getSeats());
        System.out.println("Seats: " + car.getSeats());

    }
}

在构造者模式内我们可以把校验逻辑放置到build() 方法中,先创建建造者,并且通过 set() 方法设置建造者的变量值,然后在使用 build() 方法真正创建对象之前,做集中的校验,校验通过之后才会创建对象。除此之外,我们把 ResourcePoolConfig 的构造函数改为 private 私有权限。 这样我们就只能通过建造者来创建 ResourcePoolConfig 类对象。并且, ResourcePoolConfig 没有提供任何 set() 方法,这样我们创建出来的对象就是不可变对象了。 通过构造者模式,我们可以更加灵活地创建对象, 而且代码更易读,更易于维护,现在使用的okhttp,workmanger,Spring Framework等框架,都是使用构造者模式。

总结下:

  1. 构造者模式

    • 构造者模式用于创建一个复杂对象,这个对象可能由多个部分组成,而且创建过程中的配置选项较多。
    • 构造者模式通过将对象的构建过程拆分成多个步骤,并提供一个统一的构建接口,使得用户可以根据自己的需求逐步构建对象。
    • 构造者模式主要通过一个独立的构造者类来构建目标对象,用户可以根据需要选择不同的构建步骤和配置选项。
  2. 工厂模式

    • 工厂模式用于创建对象,但相比构造者模式,它更加关注于创建过程的封装和隐藏。
    • 工厂模式主要通过一个工厂类来创建对象,用户只需要向工厂类提出请求,而不需要关心对象的创建细节。
    • 工厂模式包括简单工厂模式、工厂方法模式和抽象工厂模式等不同的变体,它们的主要区别在于创建对象的方式和灵活性。

举例说明:

  • 假设我们要创建一个汽车对象,汽车有多个配置选项(如引擎类型、座位数、颜色等),这时候就可以考虑使用构造者模式。通过构造者模式,我们可以定义一个汽车构造者类,让用户可以根据需要逐步构建汽车对象,并设置不同的配置选项。

  • 如果我们只是根据用户输入的参数来创建相应的汽车对象,而不需要考虑对象创建的过程和复杂度,那么可以考虑使用工厂模式。例如,根据用户选择的汽车型号,工厂类可以直接创建对应的汽车对象并返回给用户,而用户不需要关心对象的创建细节。

2.构造者模式怎么用?

通过上面的对比我已经了解构造者模式是对于构造细节的进一步关注,并且针对构造提供用户可配置选择项。 使用构造者模式时需要注意以下几点:

  • 清晰的构建步骤:构造者模式的核心在于将一个复杂对象的构建过程分解成多个简单的步骤,因此需要确保每个构建步骤都清晰明了,并且步骤之间的顺序是合理的。
  • 灵活性和可扩展性:构造者模式应该具有一定的灵活性,允许用户根据需求选择不同的构建步骤和配置选项。同时,应该考虑到未来的扩展性,使得新的构建步骤或配置选项可以方便地添加进来。
  • 参数校验和合法性检查:在构造者模式中,应该对用户输入的参数进行必要的校验和合法性检查,确保构建过程中不会出现意外情况或错误。
  • 线程安全性:如果构造者模式在多线程环境下使用,需要考虑其线程安全性。可以采用同步机制或者其他线程安全的方式来确保对象的安全创建。
  • 可读性和维护性:构造者模式的代码应该具有良好的可读性和可维护性,使得其他开发人员能够轻松理解和修改代码。因此,命名规范、注释和文档等方面的考虑也很重要。

3.构造者模式有什么缺点?

了解了构造者模式的优点,也要清楚构造者模式的缺点,请你在使用的时候知道为什么使用构造者,又为什么不选择构造者?

  1. 增加了代码量:使用构造者模式会引入额外的构建者类,导致代码量增加。尤其是当对象的构建过程比较简单时,使用构造者模式可能会显得过于繁琐。
  2. 对象的构建过程可能不够直观:虽然构造者模式将对象的构建过程分解成多个步骤,但是对于一些简单的对象,使用构造者模式可能会显得过于复杂,不够直观。
  3. 不适合创建不可变对象:构造者模式通常用于创建可变对象,在对象构建完成后可以继续修改其属性。但是对于需要创建不可变对象的情况,构造者模式并不适用。
  4. 可能会引入线程安全问题:如果在多线程环境下使用构造者模式,需要确保构建者类的线程安全性。这可能需要引入额外的同步机制,增加了复杂性和开销。
  5. 可能会导致过度设计:有时候开发人员可能会过度使用构造者模式,即使对象的构建过程并不是非常复杂。这样可能会导致代码过度设计,增加了维护成本。

其中我觉得最重要的就是第2、3、4条,第1和5并不会导致代码的质量问题,只是对代码简洁性有一定的影响。而234则是会造成质量和结构上的问题。