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

TypeScript类似Java的Builder模式:父类链式调用赋值失败问题

解决TypeScript井字棋Builder模式父类属性不生效的问题

你遇到的问题核心是子类的Builder没有正确关联父类Player的symbol属性设置逻辑,导致调用setSymbol时没有把值真正赋给实例的父类属性。以下是几种不用把setter/getter移到子类的解决方法:


方法1:抽离通用PlayerBuilder基类复用逻辑

把父类属性的设置逻辑抽象到基类Builder中,子类Builder继承后只处理自身特有的属性,避免重复代码:

enum TicTacToeSymbol {
  X = 'X',
  O = 'O'
}

// 抽象父类Player
abstract class Player {
  private _symbol: TicTacToeSymbol;

  get symbol(): TicTacToeSymbol {
    return this._symbol;
  }

  set symbol(value: TicTacToeSymbol) {
    this._symbol = value;
  }
}

// 人类玩家子类
class HumanPlayer extends Player {
  private _name: string;

  get name(): string {
    return this._name;
  }

  set name(value: string) {
    this._name = value;
  }

  static builder() {
    return new HumanPlayerBuilder();
  }
}

// 通用父类Builder,处理symbol属性
abstract class PlayerBuilder<T extends Player> {
  protected player: T;

  constructor(player: T) {
    this.player = player;
  }

  setSymbol(symbol: TicTacToeSymbol): this {
    this.player.symbol = symbol;
    return this;
  }

  abstract build(): T;
}

// 人类玩家Builder,继承通用Builder并处理name属性
class HumanPlayerBuilder extends PlayerBuilder<HumanPlayer> {
  constructor() {
    super(new HumanPlayer());
  }

  setName(name: string): this {
    this.player.name = name;
    return this;
  }

  build(): HumanPlayer {
    return this.player;
  }
}

调用示例:

const human = HumanPlayer.builder().setName('XYZ').setSymbol(TicTacToeSymbol.X).build();
console.log(human.symbol); // 输出X,设置生效

方法2:直接在子类Builder中操作父类属性

如果不想额外定义基类Builder,可在子类的静态builder方法中直接处理父类属性:

enum TicTacToeSymbol {
  X = 'X',
  O = 'O'
}

abstract class Player {
  private _symbol: TicTacToeSymbol;

  get symbol(): TicTacToeSymbol {
    return this._symbol;
  }

  set symbol(value: TicTacToeSymbol) {
    this._symbol = value;
  }
}

class HumanPlayer extends Player {
  private _name: string;

  get name(): string {
    return this._name;
  }

  set name(value: string) {
    this._name = value;
  }

  static builder() {
    const player = new HumanPlayer();
    return {
      setName(name: string) {
        player.name = name;
        return this;
      },
      setSymbol(symbol: TicTacToeSymbol) {
        // 直接调用父类setter
        player.symbol = symbol;
        return this;
      },
      build() {
        return player;
      }
    };
  }
}

这种方式无需额外创建Builder类,直接在静态方法中返回包含所有属性设置逻辑的对象,同样能确保symbol被正确赋值。


方法3:子类Builder直接访问父类属性

如果用独立的Builder类实现,只要在setSymbol方法中直接通过实例调用父类的setter即可:

class HumanPlayerBuilder {
  private player: HumanPlayer;

  constructor() {
    this.player = new HumanPlayer();
  }

  setName(name: string): this {
    this.player.name = name;
    return this;
  }

  setSymbol(symbol: TicTacToeSymbol): this {
    this.player.symbol = symbol;
    return this;
  }

  build(): HumanPlayer {
    return this.player;
  }
}

// HumanPlayer类中返回Builder实例
class HumanPlayer extends Player {
  // ... 其他代码
  static builder() {
    return new HumanPlayerBuilder();
  }
}

核心逻辑都是让Builder能够正确调用父类Player的symbol setter,既保留了父类的属性复用,又解决了属性不生效的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 20:12:54