【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开发干货

相关推荐
前端Hardy6 小时前
HTML&CSS:有趣的漂流瓶
前端·javascript·css
前端Hardy6 小时前
HTML&CSS :惊艳 UI 必备!卡片堆叠动画
前端·javascript·css
无羡仙6 小时前
替代 Object.freeze 的精准只读模式
前端·javascript
小菜全7 小时前
uniapp新增页面及跳转配置方法
开发语言·前端·javascript·vue.js·前端框架
白水清风7 小时前
关于Js和Ts中类(class)的知识
前端·javascript·面试
前端Hardy7 小时前
只用2行CSS实现响应式布局,比媒体查询更优雅的布局方案
javascript·css·html
车口8 小时前
滚动加载更多内容的通用解决方案
javascript
艾小码8 小时前
手把手教你实现一个EventEmitter,彻底告别复杂事件管理!
前端·javascript·node.js
yvya_8 小时前
常见设计模式详解
设计模式