美文网首页
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