JavaDog程序狗
发布于 2026-11-02 / 1 阅读
0
0

【设计模式】跟着狗哥复习设计模式(四):建造者模式

前几天看电脑配置单,我盯着一串参数看了半天。

CPU、内存、硬盘还好,后面再跟个显卡、散热、机箱,眼神稍微一飘,就不知道哪个值该放哪儿了。

Java 里也常碰到这种情况。今天咱就拿狗哥组装电脑来说说 建造者模式。

加瓦狗在电脑店按配置单组装电脑

先看一个有点别扭的写法

假设一台电脑有四项配置:CPU、内存、硬盘、显卡。

直接用构造方法,大概会写成这样:

Computer computer = new Computer(
        "i5", "16GB", "1TB", null
);

代码能跑,但读起来不太舒坦。

"16GB" 是内存还是硬盘?最后那个 null 又代表啥?过两天再回来看,还得翻构造方法。

多参数构造方法为什么容易看乱

参数再多一点,顺序还容易传错。俩参数的小类没啥,七八个参数就真有点拧巴了。

换成一张“配置单”

电脑店装机,一般不会让你背着顺序报参数。

配置单上写清楚:CPU 选 i5,内存 16GB,硬盘 1TB。显卡暂时不要,也不影响先把机器装起来。

Builder 干的活,跟这张配置单差不多。

Computer、Builder 和调用者之间的关系

这里就两个角色:

  • Builder 暂存每一步配置;
  • Computer 保存最后装好的结果。

先别背定义。看完代码,这俩角色自然就对上了。

最小版 Builder

先把 Computer 的字段定下来。电脑一旦装好,配置就不再跟着 Builder 变化,所以字段用 final。

public final class Computer {
    private final String cpu;
    private final String memory;
    private final String disk;
    private final String graphicsCard;

    private Computer(Builder builder) {
        this.cpu = builder.cpu;
        this.memory = builder.memory;
        this.disk = builder.disk;
        this.graphicsCard = builder.graphicsCard;
    }
}

构造方法写成 private,外面不能随手 new Computer(...)。

想装电脑,统一找 Builder。

public static final class Builder {
    private String cpu;
    private String memory;
    private String disk;
    private String graphicsCard = "none";

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

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

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

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

每个方法只做一件小事:记住一项配置,然后把当前 Builder 返回去。

所以调用时可以一段接一段地写。

Builder 链式配置的执行顺序

Computer computer = new Computer.Builder()
        .cpu("i5")
        .memory("16GB")
        .disk("1TB")
        .build();

跑出来是:

CPU=i5, memory=16GB, disk=1TB, graphicsCard=none

这段比四个裸参数好认。想改内存,就找 .memory();想加显卡,就补一行 .graphicsCard("RTX 4060")。

不过有个地方容易说偏:链式调用不等于建造者模式。

真正要紧的是,Builder 先收集配置,最后再创建完整对象。链式写法只是让代码顺手些。

必填项放到 build() 里检查

电脑可以不装独立显卡,但不能没有 CPU、内存和硬盘。

这三项统一在 build() 里检查,少一项就不让出货。

必填配置与可选配置

public Computer build() {
    require(cpu, "CPU is required");
    require(memory, "memory is required");
    require(disk, "disk is required");
    return new Computer(this);
}

private static void require(String value, String message) {
    if (value == null || value.isBlank()) {
        throw new IllegalStateException(message);
    }
}

比如漏掉 CPU:

new Computer.Builder()
        .memory("16GB")
        .disk("1TB")
        .build();

程序会抛出:

java.lang.IllegalStateException: CPU is required

校验集中在一个地方,调用的人不用猜规则。这个小细节挺实用,省得半成品对象混进后面的代码。

build() 之后,电脑已经独立了

build() 每次都会创建一个新的 Computer。

后面继续修改 Builder,不会把已经装好的那台电脑偷偷改掉。

Builder 修改前后两个 Computer 互不影响

Computer.Builder builder = new Computer.Builder()
        .cpu("i5")
        .memory("16GB")
        .disk("1TB");

Computer first = builder.build();
Computer second = builder.cpu("i7").build();

System.out.println(first.getCpu());  // i5
System.out.println(second.getCpu()); // i7

第一台还是 i5,第二台才是 i7。

这里靠的是 Computer 从 Builder 里复制字段,而且自身字段不能再改。济南话说,就是各是各的,别掺和。

啥时候适合用

Builder 不是见个类就往上套。

如果一个类只有姓名、年龄两个字段,普通构造方法就挺清楚,没必要多写一层。

什么时候适合使用 Builder

我一般会看这几件事:

  • 参数已经多到不好认;
  • 有些必填,有些可选;
  • 创建前需要统一校验;
  • 希望最终对象创建后别再乱改。

碰上其中两三条,Builder 往往就比较合适。要是只有两个简单参数,咱别整复杂了。

狗哥收个尾

这篇代码不长,核心就是 先配,后建。

Builder 负责记配置,build() 负责检查并创建对象,Computer 只保存最后结果。

你可以把示例里的电脑换成订单、请求参数或导出任务,再敲一遍。字段一多,手感很快就出来了。

下一篇接着聊 原型模式。看看已经有一个对象时,怎么从它手里再复制一个。


评论