ArkTS建造者模式

ArkTS建造者模式

建造者模式的优点在于伸缩性强+线程安全,在复杂bean的初始化中经常使用,下面是一个Effective Java书中的例子

// Builder Pattern
public class NutritionFacts {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

    public static class Builder {
        // Required parameters
        private final int servingSize;
        private final int servings;
        // Optional parameters - initialized to default values
        private int calories = 0;
        private int fat = 0;
        private int sodium = 0;
        private int carbohydrate = 0;

        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings = servings;
        }

        public Builder calories(int val) {
            calories = val;
            return this;
        }

        public Builder fat(int val) {
            fat = val;
            return this;
        }

        public Builder sodium(int val) {
            sodium = val;
            return this;
        }

        public Builder carbohydrate(int val) {
            carbohydrate = val;
            return this;
        }

        public NutritionFacts build() {
            return new NutritionFacts(this);
        }
    }

    private NutritionFacts(Builder builder) {
        servingSize = builder.servingSize;
        servings = builder.servings;
        calories = builder.calories;
        fat = builder.fat;
        sodium = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
}

实现的关键是Bean只能由Builder初始化,所以Bean的构造函数私有化,保证外界不能new,但Builder作为静态内部类任然可以访问

ArkTS没有静态内部类,想实现上面的效果可以在Bean的构造函数中增加一个只能由Builder创建的token,下面是对应的例子

// 1. 定义私有令牌类(仅在模块内可见)
class ConstructionToken {
  // 私有构造函数确保只有本模块能创建实例
  private constructor() {
  }

  // 工厂方法只允许 Builder 访问
  static create(): ConstructionToken {
    return new ConstructionToken();
  }
}

// 2. NutritionFacts产品类
export class NutritionFacts {
  private _servingSize: number;

  public get servingSize(): number {
    return this._servingSize;
  }

  private _servings: number;

  public get servings(): number {
    return this._servings;
  }

  private _calories?: number;

  public get calories(): number | undefined {
    return this._calories;
  }

  private _fat?: number;

  public get fat(): number | undefined {
    return this._fat;
  }

  private _sodium?: number;

  public get sodium(): number | undefined {
    return this._sodium;
  }

  private _carbohydrate?: number;

  public get carbohydrate(): number | undefined {
    return this._carbohydrate;
  }

  /**
   * 公开构造函数,但必须提供令牌
   * @param token
   * @param servingSize
   * @param servings
   * @param calories
   * @param fat
   * @param sodium
   * @param carbohydrate
   */
  constructor(
    token: ConstructionToken, // 必须提供令牌
    servingSize: number,
    servings: number,
    calories?: number,
    fat?: number,
    sodium?: number,
    carbohydrate?: number,
  ) {
    // 验证令牌有效性
    if (!(token instanceof ConstructionToken)) {
      throw new Error("Invalid construction token");
    }
    this._servingSize = servingSize
    this._servings = servings
    this._calories = calories
    this._fat = fat
    this._sodium = sodium
    this._carbohydrate = carbohydrate
  }
}

// 3. NutritionFacts建造者
export class NutritionFactsBuilder {
  private _servingSize: number;
  private _servings: number;
  private _calories?: number;
  private _fat?: number;
  private _sodium?: number;
  private _carbohydrate?: number;

  setServingSize(servingSize: number): NutritionFactsBuilder {
    this._servingSize = servingSize;
    return this;
  }

  setServings(servings: number): NutritionFactsBuilder {
    this._servings = servings;
    return this;
  }

  setCalories(calories: number): NutritionFactsBuilder {
    this._calories = calories;
    return this;
  }

  setFat(fat: number): NutritionFactsBuilder {
    this._fat = fat;
    return this;
  }

  setSodium(sodium: number): NutritionFactsBuilder {
    this._sodium = sodium;
    return this;
  }

  setCarbohydrate(carbohydrate: number): NutritionFactsBuilder {
    this._carbohydrate = carbohydrate;
    return this;
  }

  build(): NutritionFacts {
    if (!this._servingSize || !this._servings) {
      throw new Error("servingSize和servings是必须参数");
    }

    // 获取有效构建令牌
    const token = ConstructionToken.create();

    return new NutritionFacts(
      token,
      this._servingSize,
      this._servings,
      this._calories,
      this._fat,
      this._sodium,
      this._carbohydrate
    );
  }
}