JavaScript 创建型设计模式

设计模式的目的: 解决反复出现的问题而提出的可重用解决方案

创建型设计模式: 解决对象的创建问题

Factory 工厂模式

工厂模式: 将对象创建的逻辑封装成函数

工厂函数只关心是否可以制造出实例就行, 而不关心其实现细节

创建一个测评类, 非生产模式下, 测试函数运行时间

const changeColor = color => input => `\x1b[${color}m${input}\x1b[0m`
const changeGreen = changeColor(92)
const changeViolet = changeColor(95)

class Profiler {
  constructor(label) {
    this.label = label ? label : 'timer'
    this.lastTime = null
  }

  start() {
    this.lastTime = process.hrtime()
  }

  end() {
    const diff = process.hrtime(this.lastTime)
    console.log(`${changeViolet(this.label)}: ${changeGreen(diff[0])} s ${changeGreen(diff[1])} ns`)
  }
}

const noopProfiler = {
  start() { },
  end() { }
}

const createProfiler = label => {
  if (process.env.NODE_ENV === 'production') {
    return noopProfiler
  }

  return new Profiler(label)
}

export const useProfiler = (fn, label) => {
  const profiler = createProfiler(label)
  profiler.start()
  const res = fn()
  profiler.end()
  return res
}

使用如下

import { useProfiler } from './profiler.js'

// 对给定数字进行因式分解
function getAllFactors(intNumber) {
  const factors = []
  for (let factor = 2; factor <= intNumber; factor++) {
    while ((intNumber % factor) === 0) {
      factors.push(factor)
      intNumber = intNumber / factor
    }
  }
  return factors
}

const myNumber = process.argv[2]
const myFactors = useProfiler(() => getAllFactors(myNumber), 'getAllFactors')
console.log(`Factors of ${myNumber} are: `, myFactors)

在这里插入图片描述

Builder 建造者模式

使用者可一步一步创建复杂的对象

实现一个 url 构造器, 使用如下

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

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

console.log(url.toString())	// https://user:pass@example.com
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)
  }
}

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
  }
}

Revealing Constructor 模式

只在构建中的时候, 使用私密属性

比如

new Promise((resolve, reject) => {
})

promise 有个状态, 其状态在 Promise 对象创建好之后, 便不可修改 (不考虑使用 setTimeout 等异步情况)

Singleton 单例模式

确保某个类只能出现一个实例

从模块中导出一个实例, 基本就做到了单例模式所应具备的效果

import { Database } from './Database.js'

export const dbInstance = new Database('my-app-db', {
  url: 'localhost:5432',
  username: 'user',
  password: 'password'
})

但是, 我们无法保证包与包之间的实例也相同

node_modules
-- package-a
---- node_modules
------ mydb				version: 1.0
-- package-b
---- node_modules
------ mydb				version: 2.0

在这里插入图片描述

管理模块之间的依赖

单例

导出一个实例: export default new DataBase()

在这里插入图片描述

博客模块: 从数据库中获取或插入一篇文章, 依赖于数据库模块
用户可通过博客模块提供的 API 操作数据库

import { Blog } from './blog.js'

async function main() {
  const blog = new Blog()

  await blog.insertPost(
    'dependency-injection',
    'Dependency injection in Node.js',
    'Today we will discuss about dependency injection in Node.js\n\n...',
    new Date('2022-04-08')
  )
  
  const posts = await blog.getAllPosts()
  if (posts.length === 0) {
    console.log('No post available. Run `node import-posts.js`' +
      ' to load some sample posts')
  }

  for (const post of posts) {
    console.log(post.title)
    console.log('-'.repeat(post.title.length))
    console.log(`Published on ${new Date(post.created_at)
      .toISOString()}`)
    console.log(post.content)
  }
}

main().catch(console.error)

blog.js

import db from './db.js'

export class Blog {
  insertPost(id, title, content, created_at) {
    db.insertPost(id, title, content, created_at)
  }
  getAllPosts() {
    return db.getAllPosts()
  }
}

db.js 模拟的数据库

const posts = [
  {
    id: 'my-first-post',
    title: 'My first post',
    content: 'Hello World!\nThis is my first post',
    created_at: new Date('2022-04-06')
  }
]

class MockDataBase {
  constructor() {
    this.posts = posts
  }
  insertPost(id, title, content, created_at) {
    this.posts.push({ id, title, content, created_at })
  }
  getAllPosts() {
    return this.posts
  }
}

export default new MockDataBase()

DI 依赖注入

外部传入依赖的东西

blog.js 中, blog.js 模块 与 db.js 模块之间紧密耦合, 如果没有 db.js 模块, 那么, blog.js 模块将无法正常工作

重构 blog.js

export class Blog {
  constructor(db) {
    this.db = db
  }
  insertPost(id, title, content, created_at) {
    this.db.insertPost(id, title, content, created_at)
  }
  getAllPosts() {
    return this.db.getAllPosts()
  }
}

在这里插入图片描述

具体使用 blog.js 如下

import { Blog } from './blog.js'
import db from './db.js'

async function main() {
  const blog = new Blog(db)
  // .......
}

main().catch(console.error)

来自: <Nodejs 设计模式第三版> 第七章-创建型的设计模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值