平时写JavaScript的时候,如果遇到一个对象有十几个属性,构造函数的参数列表往往会长到没法看。调用的时候不仅要记住参数顺序,还容易传错位置,排查起来相当费劲。建造者模式(Builder Pattern)就是为了解决这类问题而生的:它把一个复杂对象的构建过程拆成一个个独立的步骤,每一步只负责装配一部分内容,最后统一产出完整对象。这篇文章会从模式结构讲起,配合可运行的代码,把JS里实现建造者模式的完整步骤说清楚。

一、建造者模式的核心结构与角色分工
建造者模式属于创建型设计模式,它的核心思想是将对象的构建过程与表示分离。换句话说,同样的构建步骤,可以产出不同形态的对象。整个模式通常由四个角色组成:产品(Product)、抽象建造者(Builder)、具体建造者(ConcreteBuilder)和指挥者(Director)。
产品角色就是最终要生成的复杂对象,比如一个包含多个区块的页面配置、一份带样式和校验规则的表单。抽象建造者定义了构建产品各个部件的接口,例如buildHeader()、buildBody()这类方法。具体建造者则实现这些接口,不同的实现方式能产出不同风格的产品。指挥者负责调用建造者的方法,按固定顺序完成组装,它不关心具体细节,只负责流程编排。
这四个角色协作的流程是:客户端先创建一个具体建造者,把它交给指挥者;指挥者按既定顺序调用建造者的各个build方法;最后从建造者手中取出装配完成的产品。这种分工带来的最大好处是,产品的内部组装逻辑被封装起来了,客户端只需要说"我要什么",不需要知道"怎么拼"。
二、用JS一步步实现一个表单生成器
下面通过一个表单生成器的例子来演示完整实现步骤。假设我们要生成一个包含标题、输入框列表和提交按钮的表单配置对象。先定义产品类,它承载最终的数据结构:
// 产品角色:表单配置
class FormData {
constructor() {
this.title = '';
this.fields = [];
this.button = null;
}
show() {
console.log('表单标题:' + this.title);
this.fields.forEach(f => console.log('字段:' + f.label));
if (this.button) {
console.log('提交按钮:' + this.button.text);
}
}
}接着定义抽象建造者。由于JavaScript没有严格的接口概念,通常用一个基类声明方法,未实现的直接抛出错误,起到约束作用:
// 抽象建造者
class FormBuilder {
buildTitle(title) { throw new Error('必须实现 buildTitle'); }
buildField(field) { throw new Error('必须实现 buildField'); }
buildButton(text) { throw new Error('必须实现 buildButton'); }
getResult() { throw new Error('必须实现 getResult'); }
}
// 具体建造者:普通风格表单
class NormalFormBuilder extends FormBuilder {
constructor() {
super();
this.form = new FormData();
}
buildTitle(title) {
this.form.title = title;
return this; // 返回this支持链式调用
}
buildField(field) {
this.form.fields.push(field);
return this;
}
buildButton(text) {
this.form.button = { text: text, type: 'submit' };
return this;
}
getResult() {
return this.form;
}
}注意每个build方法都返回this,这是实现链式调用的关键。最后是指挥者,它封装了一套固定的组装流程:
// 指挥者:封装固定建造流程
class FormDirector {
constructor(builder) {
this.builder = builder;
}
construct(config) {
this.builder.buildTitle(config.title);
config.fields.forEach(field => this.builder.buildField(field));
this.builder.buildButton(config.buttonText);
return this.builder.getResult();
}
}
// 客户端调用
const builder = new NormalFormBuilder();
const director = new FormDirector(builder);
const form = director.construct({
title: '用户注册',
fields: [
{ label: '用户名', type: 'text' },
{ label: '密码', type: 'password' }
],
buttonText: '立即注册'
});
form.show();运行后就能得到一个装配完整的表单配置对象。如果之后需要一个带红色主题、字段自动加必填校验的版本,只需要再写一个RedStyleFormBuilder继承抽象建造者,指挥者的流程完全不用改动,这就是模式带来的扩展性。
三、链式调用简化版:去掉Director的写法
在前端开发中,很多时候并不需要严格的四角色结构,可以省略指挥者,直接让客户端通过链式调用组装对象。这种简化版在各类组件库和工具库里非常常见,比如jQuery的链式API就是类似思路。
// 简化的链式建造者
class QueryBuilder {
constructor(table) {
this.table = table;
this.conditions = [];
this.orderField = null;
}
where(cond) {
this.conditions.push(cond);
return this;
}
orderBy(field) {
this.orderField = field;
return this;
}
build() {
let sql = 'SELECT * FROM ' + this.table;
if (this.conditions.length) {
sql += ' WHERE ' + this.conditions.join(' AND ');
}
if (this.orderField) {
sql += ' ORDER BY ' + this.orderField;
}
return sql + ';';
}
}
// 使用
const sql = new QueryBuilder('users')
.where('age > 18')
.where('status = 1')
.orderBy('created_at')
.build();
console.log(sql);
// 输出:SELECT * FROM users WHERE age > 18 AND status = 1 ORDER BY created_at;这种写法的可读性非常好,每一步做了什么一目了然。它的代价是流程控制权交给了客户端,步骤的调用顺序不再受统一约束。如果步骤之间存在强依赖(比如必须先buildTitle才能buildField),还是建议保留Director角色,在construct方法里固定顺序,避免调用方乱序导致半成品对象。
另外提醒一点,链式建造者通常要实现一个build()或getResult()作为终点方法,它的职责是做最终校验并返回产品。如果发现必填项缺失,在这里抛出异常比让残缺对象流到下游更安全。
四、建造者模式与工厂模式的区别及选型建议
不少人会把建造者模式和工厂模式混为一谈,虽然两者都是创建型模式,但关注点完全不同。工厂模式关心的是"创建什么",根据入参返回不同类型的对象,一次调用就出结果;建造者模式关心的是"怎么创建",强调分步骤组装,调用方可以参与到构建过程中间。
可以从三个维度判断该用哪个。第一看对象复杂度:只有几个属性、一次构造就能完成的,用工厂或直接字面量即可;属性多、结构分层、需要部分组装的,用建造者。第二看是否需要控制流程:如果不同场景下组装顺序不同,或者要复用同一套组装流程,Director能发挥价值。第三看参数可读性:当构造函数参数超过四五个、且有多个同类型参数时,链式建造者的方法名天然带语义,比一长串位置参数可靠得多。
还要注意一个常见误区:不是所有链式调用都算建造者模式。判断的关键在于对象是否被拆成了多个部件逐步装配,以及是否存在一个明确的最终产出动作。像Array的map().filter()链只是普通的方法链,每次都返回新数组,并不涉及同一个复杂对象的分步构建。
总结一下,在JS中实现建造者模式的步骤可以归纳为:定义产品类承载最终数据,编写建造者类并为每个部件提供build方法,让方法返回this以支持链式调用,需要流程约束时引入Director封装组装顺序,最后提供getResult或build方法输出成品。掌握这套思路后,无论是构建复杂配置对象、拼接查询语句,还是设计组件库的初始化API,都能用上这种优雅的组织方式。
建造者模式JavaScript设计模式Builder模式修改时间:2026-09-07 21:18:41