一、 建造者模式解决了什么痛点?
建造者模式的核心思想是:将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。 简单来说,它让你能够一步步地创建复杂对象,并且可以精确控制每个部分的构建过程,最终得到一个完整的对象。
二、 建造者模式的结构
建造者模式通常包含以下几个角色:
- 产品 (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开发干货