建造者模式(Builder):产品类、抽象建造者、具体建造者、指挥者,以及链式调用实现与不可变对象构建

建造者模式,说白了就是帮你把「复杂对象的构造过程」和「它的最终表示」拆开。我刚开始接触这个模式时,总觉得它跟工厂模式差不多,后来在项目中踩过坑才明白——工厂模式是「我要一个产品,你给我造出来」,而建造者模式是「我要一个产品,但它的组装步骤很复杂,每一步我可能还想自定义」。

你想想看,如果一个对象有十几个字段,其中一半还是可选的,你怎么办?写十几个重载的构造函数?那代码简直没法看。用 setter?又破坏了不可变性。嗯,这时候建造者模式就派上用场了。

模式的核心角色

建造者模式有四个核心角色,我习惯把它们记成「一个产品、一个抽象蓝图、一个具体施工队、一个项目经理」:

  • 产品类(Product):最终要构建的那个复杂对象。它通常包含多个部件,而且这些部件可能有不同的组合方式。
  • 抽象建造者(Builder):定义构建产品各个部件的抽象接口。说白了就是「你要造这个产品,必须实现这些步骤」。
  • 具体建造者(ConcreteBuilder):实现抽象建造者的接口,负责具体部件的构建,并提供获取最终产品的方法。
  • 指挥者(Director):控制构建过程的顺序和逻辑。它不关心具体怎么造,只关心「先做什么,后做什么」。

核心要点:建造者模式最大的价值在于,它将「复杂对象的构建算法」与「部件的具体装配」解耦了。同样的构建过程,换不同的建造者,就能得到不同的产品表示。

SVG 结构图:建造者模式的核心逻辑

建造者模式结构图 产品类 (Product) - 部件A, 部件B, 部件C 抽象建造者 (Builder) + buildPartA() + buildPartB() : getResult() ConcreteBuilder1 实现所有构建步骤 ConcreteBuilder2 另一种实现方式 指挥者 (Director) + construct(builder) 控制构建顺序 创建 实现 调用构建步骤 返回产品 产品类 抽象建造者 具体建造者 指挥者

经典实现:以「计算机配置」为例

我记得有一次帮朋友组装电脑,发现这简直就是建造者模式的现实版——CPU、内存、硬盘、显卡,每个部件都可以选不同的品牌和型号,但组装流程是固定的。咱们就用这个例子来写代码。

1. 产品类:Computer

// Java 版本
public class Computer {
    private final String cpu;
    private final String memory;
    private final String hardDisk;
    private final String gpu;
    private final String powerSupply;

    // 构造函数私有,只能通过 Builder 创建
    private Computer(Builder builder) {
        this.cpu = builder.cpu;
        this.memory = builder.memory;
        this.hardDisk = builder.hardDisk;
        this.gpu = builder.gpu;
        this.powerSupply = builder.powerSupply;
    }

    // 只提供 getter,不提供 setter —— 不可变对象
    public String getCpu() { return cpu; }
    public String getMemory() { return memory; }
    public String getHardDisk() { return hardDisk; }
    public String getGpu() { return gpu; }
    public String getPowerSupply() { return powerSupply; }

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

    // 静态内部 Builder 类
    public static class Builder {
        private String cpu;
        private String memory;
        private String hardDisk;
        private String gpu;
        private String powerSupply;

        public Builder cpu(String cpu) {
            this.cpu = cpu;
            return this;
        }

        public Builder memory(String memory) {
            this.memory = memory;
            return this;
        }

        public Builder hardDisk(String hardDisk) {
            this.hardDisk = hardDisk;
            return this;
        }

        public Builder gpu(String gpu) {
            this.gpu = gpu;
            return this;
        }

        public Builder powerSupply(String powerSupply) {
            this.powerSupply = powerSupply;
            return this;
        }

        public Computer build() {
            // 可以在这里做参数校验
            if (cpu == null || memory == null) {
                throw new IllegalStateException("CPU 和内存是必选部件");
            }
            return new Computer(this);
        }
    }
}

个人习惯:我一般把 Builder 作为产品类的静态内部类。这样 Builder 可以直接访问产品的私有构造函数,而且从语义上看,Builder 和 Product 本来就是强关联的。

2. 抽象建造者与具体建造者

如果我们需要不同的「配置套餐」,比如「办公版」和「游戏版」,就可以用抽象建造者来规范流程:

// Java 版本:抽象建造者
public interface ComputerBuilder {
    ComputerBuilder buildCpu();
    ComputerBuilder buildMemory();
    ComputerBuilder buildHardDisk();
    ComputerBuilder buildGpu();
    ComputerBuilder buildPowerSupply();
    Computer build();
}

// 具体建造者:办公版
public class OfficeComputerBuilder implements ComputerBuilder {
    private Computer.Builder builder = new Computer.Builder();

    @Override
    public ComputerBuilder buildCpu() {
        builder.cpu("Intel i5");
        return this;
    }

    @Override
    public ComputerBuilder buildMemory() {
        builder.memory("16GB DDR4");
        return this;
    }

    @Override
    public ComputerBuilder buildHardDisk() {
        builder.hardDisk("512GB SSD");
        return this;
    }

    @Override
    public ComputerBuilder buildGpu() {
        builder.gpu("集成显卡");
        return this;
    }

    @Override
    public ComputerBuilder buildPowerSupply() {
        builder.powerSupply("400W");
        return this;
    }

    @Override
    public Computer build() {
        return builder.build();
    }
}

// 具体建造者:游戏版
public class GamingComputerBuilder implements ComputerBuilder {
    private Computer.Builder builder = new Computer.Builder();

    @Override
    public ComputerBuilder buildCpu() {
        builder.cpu("Intel i9");
        return this;
    }

    @Override
    public ComputerBuilder buildMemory() {
        builder.memory("32GB DDR5");
        return this;
    }

    @Override
    public ComputerBuilder buildHardDisk() {
        builder.hardDisk("1TB NVMe SSD");
        return this;
    }

    @Override
    public ComputerBuilder buildGpu() {
        builder.gpu("NVIDIA RTX 4080");
        return this;
    }

    @Override
    public ComputerBuilder buildPowerSupply() {
        builder.powerSupply("850W 金牌");
        return this;
    }

    @Override
    public Computer build() {
        return builder.build();
    }
}

3. 指挥者:Director

// Java 版本
public class ComputerDirector {
    private ComputerBuilder builder;

    public ComputerDirector(ComputerBuilder builder) {
        this.builder = builder;
    }

    // 控制构建顺序
    public Computer construct() {
        return builder
                .buildCpu()
                .buildMemory()
                .buildHardDisk()
                .buildGpu()
                .buildPowerSupply()
                .build();
    }
}

4. 客户端使用

// Java 版本
public class Client {
    public static void main(String[] args) {
        // 方式一:直接使用 Builder(链式调用)
        Computer computer1 = new Computer.Builder()
                .cpu("AMD Ryzen 7")
                .memory("32GB DDR5")
                .hardDisk("1TB SSD")
                .gpu("RTX 4070")
                .powerSupply("750W")
                .build();
        System.out.println(computer1);

        // 方式二:通过指挥者 + 具体建造者
        ComputerBuilder officeBuilder = new OfficeComputerBuilder();
        ComputerDirector director = new ComputerDirector(officeBuilder);
        Computer officeComputer = director.construct();
        System.out.println(officeComputer);

        // 换一个建造者,得到不同配置
        ComputerBuilder gamingBuilder = new GamingComputerBuilder();
        director = new ComputerDirector(gamingBuilder);
        Computer gamingComputer = director.construct();
        System.out.println(gamingComputer);
    }
}

C++ 版本实现

C++ 的实现思路完全一样,只是语法上有些差异。我个人在 C++ 项目中更喜欢用智能指针来管理产品对象的生命周期:

// C++ 版本
#include <iostream>
#include <string>
#include <memory>

class Computer {
public:
    // 前向声明 Builder
    class Builder;

    std::string getCpu() const { return cpu_; }
    std::string getMemory() const { return memory_; }
    std::string getHardDisk() const { return hardDisk_; }
    std::string getGpu() const { return gpu_; }
    std::string getPowerSupply() const { return powerSupply_; }

    void print() const {
        std::cout << "Computer: " << cpu_ << ", "
                  << memory_ << ", " << hardDisk_ << ", "
                  << gpu_ << ", " << powerSupply_ << std::endl;
    }

private:
    Computer() = default;

    std::string cpu_;
    std::string memory_;
    std::string hardDisk_;
    std::string gpu_;
    std::string powerSupply_;

    friend class Builder;
};

class Computer::Builder {
public:
    Builder& cpu(const std::string& cpu) {
        computer_.cpu_ = cpu;
        return *this;
    }

    Builder& memory(const std::string& memory) {
        computer_.memory_ = memory;
        return *this;
    }

    Builder& hardDisk(const std::string& hardDisk) {
        computer_.hardDisk_ = hardDisk;
        return *this;
    }

    Builder& gpu(const std::string& gpu) {
        computer_.gpu_ = gpu;
        return *this;
    }

    Builder& powerSupply(const std::string& powerSupply) {
        computer_.powerSupply_ = powerSupply;
        return *this;
    }

    Computer build() {
        // 参数校验
        if (computer_.cpu_.empty() || computer_.memory_.empty()) {
            throw std::runtime_error("CPU and memory are required");
        }
        return computer_;
    }

private:
    Computer computer_;
};

// 使用示例
int main() {
    Computer computer = Computer::Builder()
                            .cpu("Intel i7")
                            .memory("16GB")
                            .hardDisk("512GB SSD")
                            .gpu("RTX 3060")
                            .powerSupply("650W")
                            .build();
    computer.print();
    return 0;
}

链式调用的实现技巧

链式调用说白了就是每个 setter 方法都返回 this(或者返回 Builder 本身)。这里有个小细节:

  • 返回类型必须是 Builder 本身,不能是 void 或其它类型。
  • 方法名建议用名词,比如 .cpu().memory(),而不是 .setCpu()。这样读起来更像是在描述产品,而不是在调用方法。
  • build() 方法做最终校验。我曾经在项目中发现有人漏掉了必填字段,结果运行时才报错。所以我在 build() 里加了参数校验,提前暴露问题。

不可变对象的构建

为什么建造者模式适合构建不可变对象?因为产品类的构造函数是私有的,所有字段都在构造时通过 Builder 传入,并且只提供 getter 不提供 setter。这样一旦对象创建完成,它的状态就固定了。

我曾经踩过的坑:在 Builder 中直接暴露了产品对象的引用,导致客户端可以在 build() 之后还能修改产品状态。后来我强制在 build() 方法中做一次深拷贝,或者像上面那样,让产品类的构造函数接收 Builder 并复制所有字段,彻底切断引用。

建造者模式 vs 工厂模式

对比维度 建造者模式 工厂模式
关注点 对象的构建过程(步骤、顺序) 对象的创建结果(返回什么类型)
产品复杂度 适合复杂对象(多个部件) 适合简单对象或同一接口的不同实现
构建步骤 可以精细控制每一步 通常一步完成
链式调用 天然支持 通常不支持
典型场景 配置对象、文档生成、套餐组装 日志记录器、数据库连接、对象池

适用场景总结

什么时候该用建造者模式?我总结了三条:

  1. 对象的构造参数很多(超过4个),而且大部分是可选的。用建造者模式可以避免「 telescoping constructor 反模式」。
  2. 需要构建不可变对象。Builder 模式是构建不可变对象的标准手法,Java 标准库中的 StringBuilderStream.Builder 都是例子。
  3. 同样的构建过程可以产生不同的表示。比如同一个文档导出为 PDF、HTML、Markdown 三种格式,只需要换三个不同的建造者。

一句话总结:建造者模式把「怎么做」和「做什么」分开了。指挥者决定怎么做(构建顺序),具体建造者决定做什么(每个部件用什么料)。而链式调用和不可变对象,是这个模式最实用的两个衍生能力。

好了,这一章的内容就到这里。建造者模式在实际项目中用得非常多,尤其是配置类和 DTO 的构建。下次你写一个构造函数参数超过三个的类时,不妨试试用 Builder 来重构——你会发现代码瞬间清爽很多。