# 建造者模式

# 概念

建造者模式( Builder Pattern )是一种创建型设计模式,核心思想是将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。该模式通过分步骤构建对象,让对象创建过程更加灵活可控。

# 作用

  1. 解耦构建过程与最终表示:将对象的构造细节封装在建造者中,客户端只需关注最终结果。

  2. 灵活组合可选参数:支持按需设置属性,避免强制提供所有参数。

  3. 提高代码可读性:链式调用使代码更直观,参数设置语义清晰。

  4. 保证对象完整性:在 build() 方法中可添加校验逻辑,确保对象构建完整有效。

  5. 简化复杂对象创建:尤其适用于包含多个可选参数或需要分步初始化的对象。

# 场景

  1. 多参数对象创建:当对象包含10个以上属性,且多数为可选时。

  2. 参数组合复杂:不同场景需要不同参数组合(如电脑配置方案)。

  3. 不可变对象构建:需一次性构建完整对象,避免中途修改。

  4. 分步初始化:对象创建需要多个步骤(如 SQL 查询构建器)。

  5. 参数约束验证:需要验证参数组合的有效性(如餐品搭配规则)。

# 举例

假设创建我们要一个电脑对象,电脑有多个属性,如 CPU 、内存、硬盘、显卡等。使用建造者模式可以这样设计:

package net.feixiang.creational.builder;

/**
 * 计算机类,使用 Builder 模式创建
 * 该类包含中央处理器、内存、硬盘和显卡等属性。使用 Builder 模式可以灵活地
 * 创建 Computer 对象,允许部分属性的设置。
 */
public class Computer {
    private final String cpu;     // 中央处理器

    private final String memory;  // 内存大小

    private final String disk;    // 硬盘大小

    private final String graphicsCard;    // 显卡信息

    /**
     * 私有构造函数,使用Builder来创建Computer对象
     *
     * @param builder Builder对象
     */
    private Computer(Builder builder) {
        this.cpu = builder.cpu;
        this.memory = builder.memory;
        this.disk = builder.disk;
        this.graphicsCard = builder.graphicsCard;
    }

    /**
     * 建造者类
     */
    public static class Builder {
        private String cpu = "";          // 默认值
        private String memory = "";       // 默认值
        private String disk = "";         // 默认值
        private String graphicsCard = ""; // 默认值

        public Builder() {}

        /**
         * 设置cpu
         *
         * @param cpu 中央处理器信息
         * @return Builder对象
         */
        public Builder cpu(String cpu) {
            this.cpu = cpu;
            return this;
        }

        /**
         * 设置memory
         *
         * @param memory 内存大小
         * @return Builder对象
         */
        public Builder memory(String memory) {
            this.memory = memory;
            return this;
        }

        /**
         * 设置disk
         *
         * @param disk 硬盘大小
         * @return Builder对象
         */
        public Builder disk(String disk) {
            this.disk = disk;
            return this;
        }

        /**
         * 设置graphicsCard
         *
         * @param graphicsCard 显卡信息
         * @return Builder对象
         */
        public Builder graphicsCard(String graphicsCard) {
            this.graphicsCard = graphicsCard;
            return this;
        }

        /**
         * 构建Computer对象
         *
         * @return Computer对象
         */
        public Computer build() {
            return new Computer(this);
        }
    }

    @Override
    public String toString() {
        return "Computer{" +
                "cpu='" + cpu + '\'' +
                ", memory='" + memory + '\'' +
                ", disk='" + disk + '\'' +
                ", graphicsCard='" + graphicsCard + '\'' +
                '}';
    }
}

运行示例:

package net.feixiang.creational.builder;

/**
 * 建造者模式示例
 */
public class BuilderDemo {
    public static void main(String[] args) {
        // 配置全参数的电脑
        Computer computer1 = new Computer.Builder()
                .cpu("Intel i7")
                .memory("16G")
                .disk("1T")
                .graphicsCard("NVIDIA")
                .build();
        System.out.println(computer1);

        // 配置部分参数的电脑
        Computer computer2 = new Computer.Builder()
                .cpu("AMD Ryzen 5")
                .memory("8G")
                .build();
        System.out.println(computer2);
    }
}

控制台输出:

Computer{cpu='Intel i7', memory='16G', disk='1T', graphicsCard='NVIDIA'}
Computer{cpu='AMD Ryzen 5', memory='8G', disk='', graphicsCard=''}

在这个例子中, Computer 类是被构建的对象,它有一个内部的 Builder 类作为建造者。建造者在构造时可任意选择中央处理器、内存、硬盘和显卡进行组装,没用设置的属性将使用默认值。通过建造者模式,我们可以灵活地构建不同配置的电脑对象。

# 优势


  1. 可读性更高

    建造者模式通过链式调用的方式设置可选参数,代码更加直观和易读。而重载构造方法需要根据参数数量和顺序选择合适的构造方法,容易出错且可读性较差。

  2. 灵活性更强

    建造者模式允许逐步构建对象,可以在构建过程中根据条件动态决定是否设置某些参数。而重载构造方法需要一次性提供所有参数,无法灵活地组合参数。

  3. 可维护性更好

    当对象的属性增加或修改时,建造者模式只需要在建造者类中进行调整,而重载构造方法可能需要添加或修改多个构造方法,维护成本较高。

  4. 避免参数顺序错误

    建造者模式的每个参数都有明确的方法名,调用时不容易混淆参数顺序。而重载构造方法的参数顺序容易出错,尤其是当多个参数类型相同时。

# 反例

如果不使用建造者模式,直接通过构造函数或 setter 方法来创建对象,可能会遇到以下问题:

  1. 构造函数参数过多:如果一个对象有很多属性,构造函数的参数列表会变得很长,导致代码可读性差,维护困难。

  2. 参数顺序易错:调用者需要记住参数的顺序,容易出错。

  3. 无法灵活组合可选参数:对于有多个可选参数的情况,无法灵活地选择是否设置某个参数。

package net.feixiang.creational.builder.contrary;

/**
 * 非建造者模式示例
 * 该类展示了在没有使用建造者模式的情况下,如何创建一个计算机对象。由于构造函数
 * 参数过多且顺序容易出错,导致代码冗余和可读性差。并且新增属性时需要修改多个构
 * 造函数,增加了维护成本。
 */
public class NonBuilderComputer {
    private String cpu;          // 中央处理器
    private String memory;       // 内存
    private String disk;         // 硬盘
    private String graphicsCard; // 显卡

    // 构造函数参数过多,且顺序容易出错
    public NonBuilderComputer(String cpu, String memory, 
                                String disk, String graphicsCard) {
        this.cpu = cpu;
        this.memory = memory;
        this.disk = disk;
        this.graphicsCard = graphicsCard;
    }

    // 不同的组合需要不同的构造函数,导致代码冗余
    public NonBuilderComputer(String cpu, String memory) {
        this.cpu = cpu;
        this.memory = memory;
        this.disk = ""; // 默认值
        this.graphicsCard = ""; // 默认值
    }

    @Override
    public String toString() {
        return "NonBuilderComputer{" +
                "cpu='" + cpu + '\'' +
                ", memory='" + memory + '\'' +
                ", disk='" + disk + '\'' +
                ", graphicsCard='" + graphicsCard + '\'' +
                '}';
    }
}

在这个例子中,如果不使用建造者模式,创建电脑对象时必须提供所有参数,或者要配合构造函数的重载来使用。而且,参数顺序容易出错,导致代码的可读性和维护性变差。

综上所述,建造者模式在创建复杂对象时具有明显的优势,能够提高代码的可读性、灵活性和可维护性。

# 原理

# 建造者模式可以实现链式调用的原理是什么?


建造者模式能够实现链式调用主要得益于其建造者类中方法的返回值设计。在建造者类中,每个用于设置对象属性的方法在执行完毕后都会返回建造者对象本身(即 this ),这样就可以在同一个表达式中连续调用多个方法,从而实现链式调用。

/**
 * 设置disk
 *
 * @param disk 硬盘大小
 * @return Builder对象
 */
public Builder disk(String disk) {
    this.disk = disk;
    return this;
}

/**
 * 设置graphicsCard
 *
 * @param graphicsCard 显卡信息
 * @return Builder对象
 */
public Builder graphicsCard(String graphicsCard) {
    this.graphicsCard = graphicsCard;
    return this;
}

/**
 * 构建Computer对象
 *
 * @return Computer对象
 */
public Computer build() {
    return new Computer(this);
}

在这个例子中, disk()graphicsCard() 方法都返回了 this ,即当前 Builder 对象。因此,我们可以在创建 Computer 对象时,通过链式调用的方式依次设置各个属性,最后调用 build() 方法完成对象的创建。


总结


建造者模式将复杂对象构建与表示分离,作用是解耦构建与表示、灵活组合参数、提高可读性等,适用于多参数对象创建等场景。实现原理是通过建造者类的方法返回 this 实现链式调用,最后用 build() 方法生成对象。



微信公众号

QQ交流群
原创网站开发,偏差难以避免。

如若发现错误,诚心感谢反馈。

愿你倾心相念,愿你学有所成。

愿你朝华相顾,愿你前程似锦。