讲师中心 微信公众号

工具推荐

视频效率加速
updream

一键生成高质量推广成片

立刻MV
立刻MV

低成本歌曲 MV、视频生成

Laper
Laper

剧本策划到成片协作

AniShort
AniShort

AI短剧协同制作平台

JS如何实现建造者模式?建造者的步骤

冬静酱_3580

冬静酱_3580

发布时间:2025-08-24 10:33:01

|

553人浏览过

|

来源于php中文网

原创

建造者模式通过分离复杂对象的构建与表示,使同一构建过程可生成不同配置的对象,适用于参数多、配置灵活的场景,如前端组件、表单、API请求的构建,提升代码可读性与维护性,但应避免在简单对象上过度设计。

js如何实现建造者模式?建造者的步骤

JavaScript中实现建造者模式,核心在于将一个复杂对象的构建过程与其表示分离。说白了,就是把创建对象的那一堆零碎步骤,从对象本身的类里抽出来,放到一个专门的“建造者”手里。这样一来,你就能用同样的构建过程,生成不同配置的对象,或者反过来,用不同的步骤组合出同一种类的对象。它特别适合那些构造函数参数多到爆炸,或者对象在不同场景下需要不同初始化配置的情况。

解决方案

要实现建造者模式,我们通常需要几个角色:产品(Product)、建造者(Builder)、具体建造者(Concrete Builder)以及指挥者(Director,可选)。

在JavaScript里,这大概是这么个思路:

  1. 产品(Product): 这就是我们最终想要构建的复杂对象。它不需要知道自己是如何被构建的。

    class Car {
        constructor() {
            this.engine = null;
            this.wheels = 0;
            this.color = 'white';
            this.gps = false;
        }
    
        describe() {
            console.log(`这是一辆${this.color}的汽车,配备${this.engine}引擎,${this.wheels}个轮子,${this.gps ? '有' : '没有'}GPS。`);
        }
    }
  2. 建造者(Builder): 定义构建产品各个部分的抽象接口。在JS中,这通常是一个类,包含设置产品不同属性的方法,以及一个返回最终产品的方法。

    class CarBuilder {
        constructor() {
            this.car = new Car(); // 内部持有待构建的产品实例
        }
    
        setEngine(engineType) {
            this.car.engine = engineType;
            return this; // 链式调用
        }
    
        setWheels(count) {
            this.car.wheels = count;
            return this;
        }
    
        setColor(color) {
            this.car.color = color;
            return this;
        }
    
        addGPS() {
            this.car.gps = true;
            return this;
        }
    
        build() {
            // 在这里可以做一些最终的校验或组装工作
            if (!this.car.engine || this.car.wheels === 0) {
                console.warn("警告:汽车缺少引擎或轮子,可能无法正常行驶!");
            }
            return this.car;
        }
    }
  3. 指挥者(Director,可选): 这个角色其实不是必须的,但当你有多种标准化的构建流程时,它就显得很有用了。它知道如何使用建造者来构建特定类型的产品。它封装了构建产品的复杂性。

    class CarDirector {
        constructSportsCar(builder) {
            builder.setEngine('V8').setWheels(4).setColor('red').addGPS();
            return builder.build();
        }
    
        constructCityCar(builder) {
            builder.setEngine('Inline-4').setWheels(4).setColor('blue');
            return builder.build();
        }
    
        // 也可以有更复杂的构建方法
        constructCustomCar(builder, config) {
            if (config.engine) builder.setEngine(config.engine);
            if (config.wheels) builder.setWheels(config.wheels);
            if (config.color) builder.setColor(config.color);
            if (config.hasGPS) builder.addGPS();
            return builder.build();
        }
    }

实际使用:

// 不使用Director,直接使用Builder
const myLuxuryCar = new CarBuilder()
    .setEngine('V12')
    .setWheels(4)
    .setColor('black')
    .addGPS()
    .build();
myLuxuryCar.describe();
// 输出:这是一辆black的汽车,配备V12引擎,4个轮子,有GPS。

// 使用Director
const director = new CarDirector();
const builder = new CarBuilder();

const sportsCar = director.constructSportsCar(builder);
sportsCar.describe();
// 输出:这是一辆red的汽车,配备V8引擎,4个轮子,有GPS。

// 每次构建新对象,都需要一个新的builder实例,以避免状态污染
const cityCar = director.constructCityCar(new CarBuilder());
cityCar.describe();
// 输出:这是一辆blue的汽车,配备Inline-4引擎,4个轮子,没有GPS。

为什么在JavaScript中选择建造者模式,它解决哪些痛点?

我个人觉得,在JavaScript里,建造者模式的魅力在于它能优雅地处理那些“参数爆炸”的场景。想象一下,你有一个组件,它有十几个可选配置项,比如一个弹窗组件,它可能有标题、内容、按钮文本、点击回调、关闭图标是否显示、背景是否可点击关闭、动画类型等等。如果这些都塞进构造函数,那构造函数签名会变得无比冗长,而且你得传一堆

null
或
undefined
来跳过不需要的参数。这简直是灾难。

建造者模式直接解决了所谓的“伸缩构造函数”(Telescoping Constructor)反模式。你不再需要为不同数量的参数写一堆重载的构造函数(JS里本来就没有函数重载这回事,所以你只能用一个大对象作为参数,但这又引入了新的问题,比如类型不明确,IDE提示不友好)。通过链式调用,代码可读性一下就上去了,每次调用一个

setXxx
方法,意图都非常明确,构建过程也变得清晰可见。

它还解决了构建过程中对象状态不一致的问题。在构建完成前,产品对象可能处于一种不完整的状态。建造者模式将这个中间状态封装在建造者内部,直到

build()
方法被调用,才返回一个完整的、合法的对象。这避免了外部代码在对象未完全构建时就对其进行操作,从而减少了潜在的bug。

而且,当你的构建逻辑本身需要变动,比如有时需要构建一个“豪华版”对象,有时需要“标准版”,但它们都属于同一类产品时,建造者模式能让你在不修改产品类本身的情况下,通过不同的建造者或指挥者来灵活地实现这些变种。这对于维护和扩展来说,简直是福音。

建造者模式在前端开发中有哪些实际应用场景?

在前端开发中,我发现建造者模式特别有施展拳脚的空间,尤其是当你面对那些“巨无霸”配置对象时。

AI舌诊面诊问诊医疗系统+诊疗商城系统源码
AI舌诊面诊问诊医疗系统+诊疗商城系统源码

AI 舌诊面诊问诊医疗系统 + 诊疗商城系统源码是一套结合 AI 辅助诊断与商品转化的健康类程序,基于浩创云、ThinkPHP(PHP)开发,支持微信服务号与 H5 端,适合做中医舌诊面诊问答、健康建议推送以及配套诊疗商品的运营。

下载

一个很典型的例子是UI组件的配置。比如,你需要创建一个高度可定制的图表组件。这个图表可能有不同的数据源、图例位置、轴线样式、工具提示格式、交互行为等等。如果直接通过一个巨大的配置对象来初始化,你会发现那个配置对象会变得非常复杂,而且每次使用时,你可能只关心其中一小部分。用建造者模式,你可以这样写:

// 假设 ChartBuilder 已经定义好
const myChart = new ChartBuilder()
    .setData(someDataSource)
    .setType('bar')
    .setAxisLabelsVisible(true)
    .setLegendPosition('top-right')
    .enableTooltip()
    .addClickEventHandler(handleBarClick)
    .build();

这比直接传一个

{ data: ..., type: 'bar', axisLabelsVisible: true, ... }
的对象要清晰得多,而且具备更好的智能提示。

另一个场景是复杂表单的动态构建。想象一个表单,根据用户角色或业务逻辑,需要动态地添加或移除字段,甚至调整字段的校验规则和布局。你可以有一个

FormBuilder
,通过一系列
addField()
,
addValidation()
,
setLayout()
等方法来逐步构建出最终的表单配置或DOM结构。

再比如,API请求的构建。如果你在做一个需要与多个后端服务交互的客户端,每个服务的API请求头、认证方式、查询参数、请求体结构可能都有细微差异。一个

HttpRequestBuilder
就能很好地管理这些复杂性,比如:

const apiRequest = new HttpRequestBuilder()
    .setMethod('POST')
    .setEndpoint('/api/v1/users')
    .setAuthToken(userToken)
    .addHeader('X-Custom-Header', 'value')
    .setBody({ name: 'John Doe', email: 'john@example.com' })
    .setTimeout(5000)
    .build();
// 然后你可以用这个 request 对象去发送请求

这些场景都体现了建造者模式的核心价值:将复杂对象的构建过程模块化、可读化,并提供灵活的配置方式。

如何避免滥用建造者模式,以及其潜在的局限性?

建造者模式确实好用,但任何模式都有其适用范围和潜在的坑。我见过一些项目,为了“模式化”而模式化,结果把简单的事情搞复杂了。

最大的一个误区就是过度设计(Over-engineering)。如果你的对象非常简单,只有两三个属性,而且构造逻辑也很直白,那直接用构造函数或者一个简单的工厂函数就足够了。引入建造者模式会凭空增加好几个类或对象,代码量和概念复杂度都上去了,收益却微乎其微。这就像为了拧一个螺丝,非得搬出一整套精密工具箱,结果效率反而降低了。

再来就是增加了代码的间接性。你不再是直接

new Product()
,而是
new Builder().setX().build()
。多了一层抽象,对于那些不熟悉模式的开发者来说,理解起来可能需要一点时间。调试时,调用栈也会深一层。这在团队协作时需要权衡,是否所有成员都能快速理解和维护这种结构。

另一个值得注意的点是状态管理。一个建造者实例通常会维护一个内部的产品实例。如果你不小心重复使用同一个建造者实例去构建不同的产品,就可能出现状态污染的问题,导致构建出的对象不是你期望的。所以,每次构建一个新对象时,通常都需要创建一个新的建造者实例,就像我在示例代码中

new CarBuilder()
那样。这不算局限性,但确实是一个需要留意的点。

此外,建造者模式关注的是“如何构建”,而不是“构建什么”。如果你需要根据不同条件创建完全不同类型的对象(比如,有时创建

Car
,有时创建
Motorcycle
),那么工厂模式(Factory Method 或 Abstract Factory)可能更适合。建造者模式的强项在于构建同一类型的复杂对象,但通过不同步骤或参数组合出不同的“形态”。

总的来说,判断是否使用建造者模式,我通常会问自己几个问题:

  1. 这个对象的构造函数参数是不是多到让人头疼?
  2. 这个对象在不同场景下,是不是需要多种不同的配置组合?
  3. 对象的构建过程本身是否复杂,或者涉及多步操作?
  4. 我是否需要一个机制,来确保只有当所有必要部分都构建完成后,才返回一个完整有效的对象?

如果这些问题的答案都是肯定的,那么建造者模式多半是个好选择。否则,保持简单可能是更好的策略。

热门AI工具

更多
PixTV
PixTV Hot

PixTV是一款面向AIGC内容创作的AI视频生成工具。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

讯飞绘文

讯飞绘文是一款由科大讯飞推出的一站式 AIGC 内容运营平台。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

UP简历
UP简历 Hot

一款AI办公效率工具,主要用于基于AI技术的免费在线简历制作工具,适合需要提升相关任务效率的用户。

音述AI
音述AI Hot

一款AI音频处理工具,主要用于音述AI是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区,适合需要提升相关任务效率的用户。

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

569

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

1698

2024.03.01

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

3248

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

5509

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

3909

2025.12.29

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

506

2026.01.19

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

7047

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

2888

2023.08.10

Go-Zero API开发使用教程
Go-Zero API开发使用教程

本专题聚焦Go-Zero的API开发,拆解.api描述文件的语法结构、路由与分组定义、参数绑定与校验规则,说明统一响应体、JWT鉴权、跨域与接口文档生成的落地方式,并给出网关聚合与接口测试的实践建议。

40

2026.11.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go-Zero goctl安装与使用手册
Go-Zero goctl安装与使用手册

共0课时 | 0人学习

DBeaver生态工具dbvr官方手册
DBeaver生态工具dbvr官方手册

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn