Skip to content

生成器模式

生成器模式是一种创建型设计模式,使你能够分步创建复杂对象。

使用场景:当你尝试构建一个拥有许多配置选项的对象时,该模式会特别有用。

生成器模式的主要角色如下:

  1. 生成器:接口生命再所有类型生成器中通用的产品构造步骤
  2. 具体生成器:提供构造过程的不同实现。具体生成器也可以构造不遵循通用接口的产品
  3. 产品:是最终生成的对象。由不同生成器构造的产品无需属于同一类层次构造或接口
  4. 指挥者<可选>:定义调用构造步骤的顺序,这样你就可以创建和服用特定的产品配置
  5. 客户端:必须将某个生成器对象与主管类关联,一般情况下,你只需要通过指挥者类构造函数的参数进行一次性关联即可

什么是复杂对象?:当一个类的构造函数接收很多个参数,且有些参数是可选的。那么当实列化这个类时就要传递很多类参数给构造函数,而且当有些属性有值,有些属性无值时,传递参数就会像下面这个情况:

typescript
new Computer("Intel i7", 16, "512GB SSD", "NVIDIA GTX 3060", 
             "Windows 11", true, false, true, "Wi-Fi 6", "Bluetooth 5.0", 
             null, null, ...);

写法不仅难以阅读,还容易传错参数。

生成器模式的目的是实现分步创建对象,通过分布设置属性的方式为可选属性赋值。提高代码灵活性与可读性。如:

typescript
const computer = new ComputerBuilder()
  .setCpu("Intel i7")
  .setRam(16)
  .setStorage("512GB SSD")
  .setGpu("NVIDIA GTX 3060")
  .build();

实现过程

有一个产品类如下:

typescript
class Computer {
  private cpu: string
  private ram: string
  private usbCount?: number
  private keyboard?: string
  private display?: string
  
  constructor(builder: ComputerBuilder) {
    //因为产品跟其对应的构造器的属性是一样的,所以直接拿构造器的属性覆盖产品属性
    Object.assign(this, builder)
  }

  /**
   * 获取可选参数<ram>的值
   * @returns
   */
  public getRam() {
    return this.ram
  }

  /**
   * 获取可选参数<cpu>的值
   * @returns
   */
  public getCpu() {
    return this.cpu
  }

  /**
   * 获取可选参数<usbCount>的值
   * @returns
   */
  public getUsbCount() {
    return this.usbCount
  }

  /**
   * 获取可选参数<usbCount>的值
   * @returns
   */
  public getKeyboard() {
    return this.keyboard
  }

  /**
   * 获取可选参数<dispaly>的值
   * @returns
   */
  public getDisplay() {
    return this.display
  }
}

为这个产品设立一个构造器:构造器的最终目的是返回产品对象。代码如下:

typescript
class ComputerBuilder {
    //构造器的属性特性需要与产品的属性特性保持一致,如产品的属性是可选的,那么构造器对应的属性也要是可选的
  public cpu: string
  public ram: string
  public usbCount?: number
  public keyboard?: string
  public display?: string
  constructor() {}

  /**
   * 为必选参数赋值
   * @param cpu
   * @param ram
   */
  public Builder(cpu: string, ram: string) {
    this.cpu = cpu
    this.ram = ram
    return this
  }

  /**
   * 为可选属性<usbCount>单独赋值,并返回当前对象
   * @param v
   * @returns this
   */
  public setUsbCount(v: number) {
    this.usbCount = v
    return this
  }

  /**
   * 为可选属性<keyboard>单独赋值,并返回当前对象
   * @param v
   * @returns
   */
  public setKeyboard(v: string) {
    this.keyboard = v
    return this
  }

  /**
   * 为可选属性<display>单独赋值,并返回当前对象
   * @param v
   * @returns
   */
  public setDispaly(v: string) {
    this.display = v
    return this
  }

  /**
   * 执行构建,或者说完成构建
   * @returns
   */
  public build() {
    return new Computer(this)
  }
}

使用构造器分步创建不同对象:

typescript
//创建一个计算机实例,只有必选参数的对象
const $instance = new ComputerBuilder.Builder('因特尔', '三星').build()
console.log('[ $instance ] >', $instance)

//创建一个计算机实例,有必选和一个可选参数的实例
const $instance2 = new ComputerBuilder.Builder('因特尔', '三星').setKeyboard('雷蛇').build()
console.log('[ $instance2 ] >', $instance2.getCpu(), $instance2.getKeyboard())

代码更容易维护。