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

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

作者: rollingstarky | 来源:发表于2022-07-18 19:57 被阅读0次

    State 模式是一种特殊形式的 Strategy 模式:Context 选择的具体策略根据不同的 state 发生变化。
    对于 Strategy 模式,可以基于不同的变量比如传入的参数来决定选择具体哪个策略,一旦选择确定后,直到 context 剩余的整个生命周期结束,该策略都保持不变。相反在 State 模式中,策略(或者在这里的语境下,叫做状态)在 context 的生命周期里是动态变化的,从而允许对象的行为可以根据内部状态的变化自适应地更改。

    State pattern

    举例来说,我们需要创建一个宾馆预定系统,由一个 Reservation 类对预定房间的行为进行建模。

    考虑如下一系列事件:

    • 当 reservation 对象初次创建后,其处于未确认状态。用户可以通过一个 confirm() 方法对此次预定进行确认。但不能通过 cancel() 方法取消预订,因为此次预定还并没有被确认。可以使用 delete() 方法删除这条记录
    • 一旦该 reservation 被确认,订单处于已确认状态。confirm() 方法将不能再次被调用(不能重复确认);但该 reservation 支持通过 cancel() 方法进行取消,同时该记录无法被删除(已经有人确认预定)
    • 在 reservation 日期的前一天,订单处于已生效状态。上述所有 3 个方法都不再支持,用户只能办理入住
    State pattern

    参考上图,可以实现 3 种 不同的策略,他们都实现了 confirm()cancel()delete() 这几个方法。每种策略的具体逻辑由不同的状态决定。Reservation 对象只需要在每次状态切换时,激活对应的策略。

    实例:failsafe socket

    mkdir state && cd state
    npm install json-over-tcp-2
    

    package.json

    {
      "type": "module",
      "dependencies": {
        "json-over-tcp-2": "^0.3.5"
      }
    }
    

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

    上述 FailsafeSocket 类主要由以下几个组件构成:

    • 构造函数 constructor 会初始化一个 queue 队列来存储 socket 离线时发送的数据,还创建了 offlineonline 两种不同的状态,分别对应离线时和在线时 socket 的不同行为
    • changeState() 方法负责不同状态的切换。它会更新当前状态 currentState 并调用该状态的 activate() 方法
    • send() 方法包含 FailsafeSocket 类的主要功能,它会基于在线或离线状态触发不同的行为。这里它将具体的操作指派给了当前激活的状态对象去实现

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

    上述模块负责定义 socket 处于离线状态时的行为。

    • send() 方法只负责将接受到的数据存储到队列中,因为此时是离线状态,队列中的数据会在 socket 在线时取出并发送
    • activate() 方法会尝试建立连接,连接失败则隔一秒重试。成功建立连接后,failsafeSocket 的状态变为在线状态,触发在线状态的 activate() 方法

    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.pop()
          }
        })
      }
    
      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')
        })
      }
    }
    

    OnlineState 模块实现了当 socket 处于在线状态时的行为。

    • send() 方法会将数据放入队列,并立即尝试将其写入到 socket,因为此时是在线状态。若该数据成功写入,则将其从队列中移除
    • activate() 方法会在连接成功建立时触发,会尝试发送在 socket 离线时排队的所有数据并监听任意错误事件。若有错误发生,转换到离线状态,触发离线状态的 activate 方法,继续尝试建立连接

    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'))
    

    client.js

    import {FailsafeSocket} from './failsafeSocket.js'
    
    const failsafeSocket = new FailsafeSocket({port: 5000})
    setInterval(() => {
      failsafeSocket.send(process.memoryUsage())
    }, 1000)
    

    参考资料

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

    相关文章

      网友评论

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

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