【TS 设计模式完全指南】从零到一:掌握TypeScript建造者模式,让你的对象构建链式优雅

一、 建造者模式解决了什么痛点?

建造者模式的核心思想是:将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。 简单来说,它让你能够一步步地创建复杂对象,并且可以精确控制每个部分的构建过程,最终得到一个完整的对象。

二、 建造者模式的结构

建造者模式通常包含以下几个角色:

  • 产品 (Product): 被构建的复杂对象。它不需要知道自己的构建过程,只管暴露所需的属性和方法。
  • 抽象建造者 (Builder): 一个抽象接口(或抽象类),定义了创建产品各个部件的抽象方法,以及一个获取最终产品的方法。
  • 具体建造者 (Concrete Builder): 实现 Builder 接口,负责具体产品的构建。它知道如何构建和组装产品的各个部件,并提供获取最终构建产品的实例。通常,具体建造者的方法会返回 this,以支持链式调用。
  • 指挥者 (Director): (可选) 负责安排构建过程的顺序。它接收一个 Builder 对象作为参数,然后调用其构建部件的方法,按照特定的步骤进行构建。指挥者隐藏了建造的细节,客户端只需指定指挥者和选择具体建造者即可。

三. 示例:构建一台电脑

假设我们要构建一台电脑 (Computer)。一台电脑可以包含CPU、内存、硬盘、显卡、操作系统等组件,其中一些组件是可选的,或者有多种配置。

typescript 复制代码
// 1. 产品 (Product)
class Computer {
    private cpu: string = "未知";
    private ram: string = "未知";
    private storage: string = "未知";
    private gpu: string = "集成显卡";
    private os: string = "未安装操作系统";
    private accessories: string[] = [];

    // 属性的 setters,通常不会对外暴露,由Builder内部调用
    public setCPU(cpu: string): void {
        this.cpu = cpu;
    }

    public setRAM(ram: string): void {
        this.ram = ram;
    }

    public setStorage(storage: string): void {
        this.storage = storage;
    }

    public setGPU(gpu: string): void {
        this.gpu = gpu;
    }

    public installOS(os: string): void {
        this.os = os;
    }

    public addAccessory(accessory: string): void {
        this.accessories.push(accessory);
    }

    public showConfiguration(): void {
        console.log("--- 电脑配置 ---");
        console.log(`CPU: ${this.cpu}`);
        console.log(`RAM: ${this.ram}`);
        console.log(`存储: ${this.storage}`);
        console.log(`GPU: ${this.gpu}`);
        console.log(`操作系统: ${this.os}`);
        console.log(`配件: ${this.accessories.length > 0 ? this.accessories.join(', ') : '无'}`);
        console.log("-----------------");
    }
}

// 2. 抽象建造者 (Builder Interface)
interface IComputerBuilder {
    setCPU(cpu: string): this; // 返回 this 支持链式调用
    setRAM(ram: string): this;
    setStorage(storage: string): this;
    setGPU(gpu: string): this; // 可选
    installOS(os: string): this; // 可选
    addAccessory(accessory: string): this; // 可选,多次调用
    build(): Computer; // 返回最终的产品
}

// 3. 具体建造者 (Concrete Builder)
class DesktopComputerBuilder implements IComputerBuilder {
    private computer: Computer| null = null;

    constructor() {
        this.reset();
    }

    public reset(): void {
        this.computer = new Computer();
    }

    public setCPU(cpu: string): this {
        this.computer!.setCPU(cpu);
        return this;
    }

    public setRAM(ram: string): this {
        this.computer!.setRAM(ram);
        return this;
    }

    public setStorage(storage: string): this {
        this.computer!.setStorage(storage);
        return this;
    }

    public setGPU(gpu: string): this {
        this.computer!.setGPU(gpu);
        return this;
    }

    public installOS(os: string): this {
        this.computer!.installOS(os);
        return this;
    }

    public addAccessory(accessory: string): this {
        this.computer!.addAccessory(accessory);
        return this;
    }

    public build(): Computer {
        const result = this.computer!;
        this.reset(); // 构建完成后,重置建造者,以便下次使用
        return result;
    }
}

// 4. 指挥者 (Director) - 可选
class ComputerAssembler {
    private _builder: IComputerBuilder;

    constructor(builder: IComputerBuilder) {
        this._builder = builder;
    }

    public setBuilder(builder: IComputerBuilder): void {
        this._builder = builder;
    }

    public get builder(): IComputerBuilder {
        return this._builder;
    }

    // 组装一台标准办公电脑
    public assembleOfficePC(): void {
        this.builder
            .setCPU("Intel Core i5-12400")
            .setRAM("16GB DDR4")
            .setStorage("512GB NVMe SSD")
            .installOS("Windows 10 Pro");
            // No GPU, no accessories for basic office
    }

    // 组装一台高端游戏电脑
    public assembleGamingPC(): void {
        this.builder
            .setCPU("Intel Core i9-13900K")
            .setRAM("32GB DDR5")
            .setStorage("2TB NVMe SSD + 4TB HDD")
            .setGPU("NVIDIA GeForce RTX 4090")
            .installOS("Windows 11 Home")
            .addAccessory("RGB Mechanical Keyboard")
            .addAccessory("Gaming Mouse");
    }
}

// 客户端使用
console.log("--- 电脑构建示例 ---");

// 方式一:直接使用具体建造者(更灵活)
const customBuilder = new DesktopComputerBuilder();
const myCustomPC: Computer = customBuilder
    .setCPU("AMD Ryzen 7 7700X")
    .setRAM("32GB DDR5")
    .setStorage("1TB NVMe SSD")
    .setGPU("AMD Radeon RX 7900 XTX")
    .installOS("Ubuntu 22.04 LTS")
    .addAccessory("4K Monitor")
    .build();
myCustomPC.showConfiguration();

// 方式二:通过指挥者构建(适用于固定配置的场景)
const assembler = new ComputerAssembler(new DesktopComputerBuilder());

// 构建一台办公电脑
assembler.assembleOfficePC();
const officePC = assembler.builder.build(); // 注意:这里需要强制类型转换,因为Director没有直接暴露builder的build方法
officePC.showConfiguration();

// 构建一台游戏电脑
assembler.assembleGamingPC();
const gamingPC = assembler.builder.build();
gamingPC.showConfiguration();

通过这个例子,我们可以看到:

  • Computer 类自身变得非常纯粹,只关注自己的属性和行为,不关心如何被构建。
  • DesktopComputerBuilder 提供了清晰的、链式的 API 来一步步配置电脑的每个组件。
  • ComputerAssembler 作为指挥者,定义了构建特定类型电脑(如办公型、游戏型)的标准流程,客户端无需关心具体部件的组合。

四、 建造者模式的优缺点

优点:

  • 分离构建与表示: 将复杂对象的构建过程与它的表示分离,使产品类更简洁,客户端代码更清晰。
  • 细粒度控制: 客户端可以精确控制产品的每一个构建步骤。
  • 减少构造函数参数: 避免了"伸缩构造器"的出现,使类的构造函数更简洁。
  • 更好的可读性: 链式调用使得构建过程像自然语言一样易读。
  • 保证一致性: 产品只有在完全构建完成后才通过 build() 方法返回,确保其状态的完整性和一致性。
  • 易于扩展: 可以轻松添加新的具体建造者来创建不同类型的产品表示,或者在不修改 Director 的情况下修改特定建造者的行为。

缺点:

  • 增加了类的数量和设计的复杂性: 对于简单的对象构建,引入建造者模式可能会显得冗余,增加了额外的 Builder 接口和 ConcreteBuilder 类。
  • 当产品内部结构变化时,需要修改Builder接口和所有ConcreteBuilder的实现。

五、 建造者模式与工厂模式、原型模式的区别

  • 建造者模式 (Builder Pattern): 关注的是如何一步步构建一个复杂的对象 ,并生成不同的表示。它分解了对象的创建过程。
  • 工厂方法模式 (Factory Method Pattern): 关注的是创建哪种产品(通常是单一产品),由子类决定实例化哪个具体类。
  • 抽象工厂模式 (Abstract Factory Pattern): 关注的是创建一系列相关或相互依赖的产品家族

可以说,工厂模式是一步创建 ,而建造者模式是多步创建

为了方便大家学习和实践,本文的所有示例代码和完整项目结构都已整理上传至我的 GitHub 仓库。欢迎大家克隆、研究、提出 Issue,共同进步!

📂 核心代码与完整示例: GoF

总结

如果你喜欢本教程,记得点赞+收藏!关注我获取更多JavaScript/TypeScript开发干货

相关推荐
Hilaku35 分钟前
我用 Gemini 3 Pro 手搓了一个并发邮件群发神器(附源码)
前端·javascript·github
全栈前端老曹1 小时前
【包管理】npm init 项目名后底层发生了什么的完整逻辑
前端·javascript·npm·node.js·json·包管理·底层原理
YUEchn1 小时前
无处不在的Agent
设计模式·llm·agent
HHHHHY1 小时前
mathjs简单实现一个数学计算公式及校验组件
前端·javascript·vue.js
iReachers1 小时前
HTML打包APK(安卓APP)中下载功能常见问题和详细介绍
前端·javascript·html·html打包apk·网页打包app·下载功能
愈努力俞幸运1 小时前
vue3 demo教程(Vue Devtools)
前端·javascript·vue.js
Wect1 小时前
LeetCode 274. H 指数:两种高效解法全解析
算法·typescript
持续前行1 小时前
在 Vue3 中使用 LogicFlow 更新节点名称
前端·javascript·vue.js
Anita_Sun1 小时前
Underscore.js 整体设计思路与架构分析
前端·javascript
Java陈序员1 小时前
告别手写礼簿!一款开源免费的电子红白喜事礼簿系统!
javascript·css·html