0
点赞
收藏
分享

微信扫一扫

JavaScript 行为型设计模式

最后的执着 2022-04-08 阅读 44

怎样将对象组合起来, 并规定这些对象之间的交流方式, 使其易扩展, 复用, 适配

Strategy 策略模式

将某对象逻辑里面可能发生变化的部分单独抽出来, 这部分可用各种策略来实现, 不同的策略, 该对象表现不同的行为

比如: 汽车换轮胎

  • 平常: 使用普通轮胎,

  • 雪天: 使用雪地轮胎

我们不需要仅仅因为换轮胎, 就把整个车都换掉

// Car 中轮胎是可能发生变化的部分, 将其单独抽出来
class Car {
 constructor(trie) {
   this.tire = trie
 }
 getTire() {
   return this.tire
 }
}

// 根据不同的天气, 得到不同的轮胎
class Tire {
 constructor(weather) {
   this.weather = weather
 }
 getNormalTire() {
   return 'normalTire'
 }
 getSnowTire() {
   return 'snowTire'
 }
 getTire() {
   const weatherNeedTire = {
     normal: this.getNormalTire,
     snow: this.getSnowTire,
   }
   return weatherNeedTire[this.weather]()	// 匹配需要的天气, 才会运行 getXXXTire 方法, 懒加载
 }
}

function main() {
 const normalTire = new Tire('normal').getTire()
 const normalCar = new Car(normalTire)
 console.log(normalCar.getTire())
 
 const snowTire = new Tire('snow').getTire()
 const snowCar = new Car(snowTire)
 console.log(snowCar.getTire())
}

main()

State 状态模式

不同的状态, 表现出与该状态相对应的行为

State 为 Strategy 的特例: 举个夸张😏 的比方, 还是上面汽车轮胎的例子, 假设车子的状态会发生变化

  • 当车子为普通车, 轮子为普通轮子
  • 当车子为雪地车, 轮子为雪地轮子

另一个例子: 构建一个服务器, 接受客户端发送的数据, 并打印出来

server.js

import jsonOverTcp from 'json-over-tcp-2'

const server = jsonOverTcp.createServer({ port: 5000 })
server.on('connection', socket => {
  socket.on('data', data => {
    console.log('Client data', data)
  })
})

server.listen(5000, () => console.log('Server started'))

客户端: 每隔 1 秒制造一条数据, 并调用 send 函数, send 函数 在服务器在线和离线时, 表现出不同的行为

  • 服务器在线的时候: 将数据存入 queue 中, 并读取 queue 中的数据, 将其发送给服务器, 并将 queue 中的数据 pop 出来
  • 服务器离线的时候: 将数据存入 queue 中

初始认为服务器离线, 状态转移时, 执行 active 函数, 其行为如下

离线 => 在线: 读取 queue 中所有数据, 递归将其发送给服务器, 并将其 pop 出来

在线 => 离线: 每隔 1秒, 尝试连接服务器

client.js

import { FailsafeSocket } from './failsafeSocket.js'

const failsafeSocket = new FailsafeSocket({ port: 5000 })

setInterval(() => {
  // send current memory usage
  failsafeSocket.send(process.memoryUsage())
}, 1000)

failsafeSocket.js 初始状态为离线

import { OfflineState } from './offlineState.js'
import { OnlineState } from './onlineState.js'

export class FailsafeSocket {
  constructor (options) {
    this.options = options
    this.queue = []
    this.currentState = null
    this.socket = null
    this.states = {
      offline: new OfflineState(this),
      online: new OnlineState(this)
    }
    this.changeState('offline')
  }

  changeState (state) {
    console.log(`Activating state: ${state}`)
    this.currentState = this.states[state]
    this.currentState.activate()
  }

  send (data) {
    this.currentState.send(data)
  }
}

offlineState.js

import jsonOverTcp from 'json-over-tcp-2'

export class OfflineState {
  constructor (failsafeSocket) {
    this.failsafeSocket = failsafeSocket
  }

  send (data) {
    this.failsafeSocket.queue.push(data)
  }

  activate () {
    const retry = () => {
      setTimeout(() => this.activate(), 1000)
    }

    console.log('Trying to connect...')
    this.failsafeSocket.socket = jsonOverTcp.connect(
      this.failsafeSocket.options,
      () => {
        console.log('Connection established')
        this.failsafeSocket.socket.removeListener('error', retry)
        this.failsafeSocket.changeState('online')
      }
    )
    this.failsafeSocket.socket.once('error', retry)
  }
}

onlineState.js

export class OnlineState {
  constructor (failsafeSocket) {
    this.failsafeSocket = failsafeSocket
    this.hasDisconnected = false
  }

  send (data) {
    this.failsafeSocket.queue.push(data)
    this._safeWrite(data)
  }

  _safeWrite (data) {
    this.failsafeSocket.socket.write(data, (err) => {
      if (!this.hasDisconnected && !err) {
        this.failsafeSocket.queue.shift()
      }
    })
  }

  activate () {
    this.hasDisconnected = false
    for (const data of this.failsafeSocket.queue) {
      this._safeWrite(data)
    }

    this.failsafeSocket.socket.once('error', () => {
      this.hasDisconnected = true
      this.failsafeSocket.changeState('offline')
    })
  }
}

Template 模板模式

定义一个抽象类, 用来实现某个组件的裤架, 并留下一些还未实现的步骤, 让子类去做, 这样的步骤称为模板方法 (类似 Java 中的接口)

class Car {
  constructor(trie) {
    this.tire = trie
  }
  getTire() {
    return this.tire
  }
}

class TireTemplate {
  getNormalTire() {
    throw new Error('getNormalTire() must be implemented')
  }
  getSnowTire() {
    throw new Error('getSnowTire() must be implemented')
  }
  getTire() {
    throw new Error('getTire() must be implemented')
  }

}

class Tire extends TireTemplate {
  constructor(weather) {
    super()
    this.weather = weather
  }
  getNormalTire() {
    return 'normalTire'
  }
  getSnowTire() {
    return 'snowTire'
  }
  getTire() {
    const weatherNeedTire = {
      normal: this.getNormalTire,
      snow: this.getSnowTire,
    }
    return weatherNeedTire[this.weather]()
  }
}

function main() {
  const normalTire = new Tire('normal').getTire()
  const normalCar = new Car(normalTire)
  console.log(normalCar.getTire())

  const snowTire = new Tire('snow').getTire()
  const snowCar = new Car(snowTire)
  console.log(snowCar.getTire())
}

main()

Middleware 中间件模式

企业中的 Middleware 指: 底层机制的抽象

Nodejs 中的 Middleware 指: 底层服务应用程序之间起到粘合作用软件层 (位于两者中间的软件)

代表: express在这里, 中间件是一套服务, 这些服务形成一条管道, 用以处理服务器收到的 http 请求, 并负责给出相关的响应

var express = require('express')
var app = express()

const middleware = (req, res, next) => { 
	// TODO SOMETHING
  next() 
}
app.use(middleware )
app.use(middleware)
app.use(middleware)

app.listen(3000)

关于 next 的作用, 可以看看这篇文章: koa 洋葱模型的简单实现

Command 命令模式

将执行某动作所需要的信息一个对象封装起来, 以便稍后加以执行, 封装的对象称为command 对象

使用 Task 模式实现 Command 模式

// target 为要调用的操作
function createTask(target, ...args) {
  // 这种闭包又叫做 target 的 bound function (绑定函数)
  const closure = () => {
    target(...args)
  }
  return closure
}

const foo = a => console.log(a)

// 封装的对象, 也称为 command 对象
const task = createTask(foo, 123)
task()
// task 写法与如下写法类似
const task2 = foo.bind(null, 123)
task2()
举报

相关推荐

0 条评论