JavaScript ES6大型层级对象通用建造者模式优化咨询
改进JavaScript层级对象通用建造者的方案
你的这个通用建造者设计思路真的很棒,已经帮你省去了大量重复实现的麻烦!针对你提到的三个痛点,我整理了几个具体的改进方案,咱们逐个拆解解决:
针对缺点1:必填参数不直观
改进方案
给建造者类和构造函数添加清晰的JSDoc注释,让IDE能直接提示参数要求;同时提供静态工厂方法引导调用,让必填项一目了然。
这样一来,其他开发者不用深入看构造函数代码,光看IDE提示或者调用方式就知道哪些参数是必须传的。
针对缺点2:Builder继承方式不直观
改进方案
不要在静态getter里动态生成Builder类,而是把父类的Builder作为独立的静态类成员暴露出来。子类的Builder可以直接显式继承父类的Builder,逻辑清晰易懂,新手也能快速上手扩展。
针对缺点3:抽象类无法明确需通过建造者实例化
改进方案
用ES6的私有构造函数(#constructor)限制只能由Builder类调用,直接new抽象类会抛出明确错误;同时在类的JSDoc里标注实例化方式,再配合静态builder()方法引导调用,彻底杜绝错误的实例化方式。
优化后的完整代码
/** * 抽象基类,所有Item的父类 * @abstract * @note 请通过 {@link AbstractItem.builder} 或 {@link AbstractItem.ItemBuilder} 实例化,禁止直接new */ class AbstractItem { // 私有构造函数,仅允许Builder类调用 #constructor(build) { if (this.constructor === AbstractItem) { throw new TypeError("Oops! AbstractItem should not be instantiated directly!"); } this._id = build.id; } // 把父类Builder作为静态成员暴露,方便子类继承 static ItemBuilder = class ItemBuilder { /** * 创建Item建造者 * @param {number|string} id - 唯一标识,必填项 */ constructor(id) { if (!id) { throw new TypeError('An item must have an id!'); } this._id = id; } build() { // 调用私有构造函数创建实例 return new this.constructor.#constructor(this); } get id() { return this._id; } }; // 静态工厂方法,引导用户使用正确的实例化方式 static builder(id) { return new this.ItemBuilder(id); } doSomething() { throw new TypeError("Oops! doSomething() has not been implemented!"); } get id() { return this._id; } } /** * 抽象子类,包含名称属性的Item * @abstract * @note 请通过 {@link AbstractSubItem.builder} 或 {@link AbstractSubItem.SubItemBuilder} 实例化,禁止直接new */ class AbstractSubItem extends AbstractItem { #constructor(build) { super.#constructor(build); if (this.constructor === AbstractSubItem) { throw new TypeError("Oops! AbstractSubItem should not be instantiated directly!"); } this._name = build.name; } // 显式继承父类的ItemBuilder,逻辑清晰直观 static SubItemBuilder = class SubItemBuilder extends AbstractItem.ItemBuilder { /** * 创建SubItem建造者 * @param {number|string} id - 唯一标识,必填项 * @param {string} name - 名称,必填项 */ constructor(id, name) { super(id); if (!name) { throw new TypeError('A subitem must have a name!'); } this._name = name; } build() { return new this.constructor.#constructor(this); } get name() { return this._name; } }; static builder(id, name) { return new this.SubItemBuilder(id, name); } get name() { return this._name; } } class ConcreteItem extends AbstractItem { doSomething() { console.log('Hello world! My id is ' + this.id + '.'); } } class ConcreteSubItem extends AbstractSubItem { doSomething() { console.log('Hello world! My name is ' + this.name + ' (id: ' + this.id + ').'); } } // 两种调用方式都支持,可读性拉满 ConcreteItem.builder(1).build().doSomething(); ConcreteSubItem.builder(1, 'John').build().doSomething();
改进效果总结
- 必填参数清晰化:JSDoc会在IDE里直接提示必填项,静态
builder()方法的参数列表也一目了然; - Builder继承直观化:子类Builder直接
extends AbstractItem.ItemBuilder,逻辑清晰,新手也能快速扩展; - 实例化方式明确化:私有构造函数杜绝了直接new抽象类的错误,JSDoc和静态方法双重引导正确用法。
如果需要支持可选参数的链式调用,还可以给Builder添加类似setDescription(desc)的方法,进一步增强灵活性哦!
内容的提问来源于stack exchange,提问作者thijsfranck
相关产品推荐
相关产品推荐

