You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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();

改进效果总结

  1. 必填参数清晰化:JSDoc会在IDE里直接提示必填项,静态builder()方法的参数列表也一目了然;
  2. Builder继承直观化:子类Builder直接extends AbstractItem.ItemBuilder,逻辑清晰,新手也能快速扩展;
  3. 实例化方式明确化:私有构造函数杜绝了直接new抽象类的错误,JSDoc和静态方法双重引导正确用法。

如果需要支持可选参数的链式调用,还可以给Builder添加类似setDescription(desc)的方法,进一步增强灵活性哦!

内容的提问来源于stack exchange,提问作者thijsfranck

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:15:23