1

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

 1 year ago
source link: https://rollingstarky.github.io/2022/05/05/node-js-design-patterns-builder-pattern/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

发表于 2022-05-05

| 分类于 Program

| 0

| 阅读次数:

字数统计: 5.1k

|

阅读时长 ≈ 0:05

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:[email protected]

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK