美文网首页
Node.js 设计模式笔记 —— Builder 模式

Node.js 设计模式笔记 —— Builder 模式

作者: rollingstarky | 来源:发表于2022-05-05 22:40 被阅读0次

Builder 是一种创建型设计模式,可以通过提供简单平滑的接口来简化复杂对象的创建,允许我们一步一步的构建新对象。
最明显的需要使用 Builder 模式的时候,就是当某个类的构造函数包含了太多的参数。

比如下面的一个 Boat 类:

class Boat {
  constructor(hasMotor, motorCount, motorBrand, motorModel,
    hasSails, sailsCount, sailsMaterial, sailsColor,
    hullColor, hasCabin) {
    // ...
  }
}

调用上述 Boat 类的构造方法会导致出现非常难以阅读的代码:

const myBoat = new Boat(true, 2, 'Best Motor Co. ', 'OM123', true, 1,
  'fabric', 'white', 'blue', false)

想要提升上述构造函数的设计,首先可以将所有的参数整合到一个单一的对象中,如下:

class Boat {
  constructor(allParameters) {
    // ...
  }
}

const myBoat = new Boat({
  hasMotor: true,
  motorCount: 2,
  motorBrand: 'Best Motor Co. ',
  motorModel: 'OM123',
  hasSails: true,
  sailsCount: 1,
  sailsMaterial: 'fabric',
  sailsColor: 'white',
  hullColor: 'blue',
  hasCabin: false,
})

新版本的构造函数跟原来相比提升了不少,比如用户可以清晰地看到每个传入的参数所代表的具体含义。
但是,将所有参数都放入同一个对象后再传入构造函数的方式,也有其自身的缺点。比如要想知道每个参数的具体含义,还是需要查看类的说明文档甚至类的代码。此外,没有任何强制性的协议来引导用户创建一致的对象,假如我们指定 hasMotor: true,意味着我们同时还需要再指定 motorCountmotorBrandmotorModel 参数的值。但我们无从获取此类信息(除非查看源代码)。

Builder 模式就非常适合解决上述问题。帮助用户创建一个平滑、易读、自说明的生成对象的接口,同时为创建具有一致性的对象提供指导信息。

使用 Builder 模式的 Boat 类:

class Boat {
  constructor(allParameters) {
    // ...
  }
}

class BoatBuilder {
  withMotors(count, brand, model) {
    this.hasMotor = true
    this.motorCount = count
    this.motorBrand = brand
    this.motorModel = model
    return this
  }

  withSails(count, material, color) {
    this.hasSails = true
    this.sailsCount = count
    this.sailsMaterial = material
    this.sailsColor = color
    return this
  }

  hullColor(color) {
    this.hullColor = color
    return this
  }

  withCabin() {
    this.hasCabin = true
    return this
  }

  build() {
    return new Boat({
      hasMotor: this.hasMotor,
      motorCount: this.motorCount,
      motorBrand: this.motorBrand,
      motorModel: this.motorModel,
      hasSails: this.hasSails,
      sailsCount: this.sailsCount,
      sailsMaterial: this.sailsMaterial,
      sailsColor: this.sailsColor,
      hullColor: this.hullColor,
      hasCabin: this.hasCabin
    })
  }
}

const myBoat = new BoatBuilder()
  .withMotors(2, 'Best Motor Co. ', 'OM123')
  .withSails(1, 'fabric', 'white')
  .withCabin()
  .hullColor('blue')
  .build()

BoatBuilder 类的作用就是收集 Boat 类需要的所有参数,再通过一系列 helper 方法传递给 Boat

Builder 模式的基本规则:

  • 将主要对象的复杂构建过程拆分为几个更为易读的、更容易管理的步骤
  • 尝试创建 builder 方法,向需要创建的对象一组一组地传递相关联的参数
  • 必要的情况下,在通过 builder 方法将参数传递给需要创建的对象前,尽可能地先对参数做一些处理

实例:URL builder

创建并进入 url_builder 文件夹,编辑如下内容的 package.json 文件:

{
        "type": "module"
}

url.js

export class Url {
  constructor(protocol, username, password, hostname,
    port, pathname, search, hash) {
    this.protocol = protocol
    this.username = username
    this.password = password
    this.hostname = hostname
    this.port = port
    this.pathname = pathname
    this.search = search
    this.hash = hash

    this.validate()
  }

  validate() {
    if (!this.protocol || !this.hostname) {
      throw new Error('Must specify at least a ' + 'protocol and a hostname')
    }
  }

  toString() {
    let url = ''
    url += `${this.protocol}://`
    if (this.username && this.password) {
      url += `${this.username}:${this.password}@`
    }
    url += this.hostname
    if (this.port) {
      url += this.port
    }
    if (this.pathname) {
      url += this.pathname
    }
    if (this.search) {
      url += `?${this.search}`
    }
    if (this.hash) {
      url += `#${this.hash}`
    }
    return url
  }
}

urlBuilder.js

import { Url } from './url.js'

export class UrlBuilder {
  setProtocol(protocol) {
    this.protocol = protocol
    return this
  }

  setAuthentication(username, password) {
    this.username = username
    this.password = password
    return this
  }

  setHostname(hostname) {
    this.hostname = hostname
    return this
  }

  setPort(port) {
    this.port = port
    return this
  }

  setPathname(pathname) {
    this.pathname = pathname
    return this
  }

  setSearch(search) {
    this.search = search
    return this
  }

  setHash(hash) {
    this.hash = hash
    return this
  }

  build() {
    return new Url(this.protocol, this.username, this.password,
      this.hostname, this.port, this.pathname, this.search, this.hash)
  }
}

index.js

import {UrlBuilder} from './urlBuilder.js'

const url = new UrlBuilder()
  .setProtocol('https')
  .setAuthentication('user', 'pass')
  .setHostname('example.com')
  .build()

console.log(url.toString())

运行效果:

$ node index.js
https://user:pass@example.com

参考资料

Node.js Design Patterns: Design and implement production-grade Node.js applications using proven patterns and techniques, 3rd Edition

相关文章

网友评论

      本文标题:Node.js 设计模式笔记 —— Builder 模式

      本文链接:https://www.haomeiwen.com/subject/rqjsyrtx.html