Javascript 設計模式系統講解與應用——學習筆記10-狀態模式

zoyoy發表於2020-09-29

狀態模式

  • 一個物件有狀態變化
  • 每次狀態變化都會觸發一個邏輯
  • 不能總是用 if ... else 來控制

示例
交通紅綠燈

傳統UML類圖
在這裡插入圖片描述

JS版簡單UML圖:
在這裡插入圖片描述


// 狀態: 紅燈 黃燈 綠燈
class State {
  constructor(color) {
    this.color = color
  }
  handle(context) {
    console.log(`turn to ${this.color} light`)
    context.setState(this)
  }
}

// 主體
class Context {
  constructor() {
    this.state = null
  }
  getState() {
    return this.state
  }
  setState(state) {
    this.state = state
  }
}

// test
let context = new Context()

let green = new State('green')
let yellow = new State('yellow')
let red = new State('red')

// 綠燈亮了
green.handle(context)
console.log(context.getState()) // 列印狀態
// 紅燈亮了
red.handle(context)
console.log(context.getState()) // 列印狀態
// 黃燈亮了
yellow.handle(context)
console.log(context.getState()) // 列印狀態

輸出:
turn to green light
State { color: 'green' }
turn to red light
State { color: 'red' }
turn to yellow light
State { color: 'yellow' }

場景

  • 有限狀態機
  • 寫一個簡單的Promise
有限狀態機

// 狀態機模型
const fsm = new StateMachine({
  init: '收藏'// 初始狀態,待收藏
  transitions: [
    {
      name: 'doState',
      from: '收藏',
      to: '取消收藏'
    },
    {
      name: 'deleteStore',
      from: '取消收藏',
      to: '收藏'
    }
  ],
  methods: {
    // 監聽執行收藏
    onDoStore: function() {
      alert('收藏成功')  // 可以放post請求
      updateText()
    },
    // 監聽取消收藏
    onDeleteStore: function() {
      alert('已取消收藏')  // 可以放post請求
      updateText()
    }
  }
})

const $btn = ${'#btn'}

// 點選事件
$btn.click(function () {
  if (fsm.is('收藏')) {
    fsm.doStore()
  } else {
    fsm.deleteStore()
  }
})

// 更新文案
funciton updateText() {
  $btn.text(fsm.state)
}

// 初始化文案
updateText()
寫一個簡單的Promise

Promise就是有限狀態機

  • Promise三種狀態: pending fullfilled rejected
  • pending -> fullfilled 或者 pending -> rejected
  • 不能逆向變化
// 模型
const fsm = new StateMachine({
  init: 'pending',
  transitions: [
    {
      name: 'resolve',
      from: 'pending',
      to: 'fullfilled'
    },
    {
      name: 'reject',
      from: 'pending',
      to: 'rejected'
    }
  ],
  methods: {
    // 成功
    onResolve: function (state, data) {
      // 引數: state - 當前狀態示例: data - fsm.resolve(xxx) 執行傳遞過來的引數
      data.successList.forEach(fn => fn())
    },
    // 失敗
    onReject: function (state, data) {
      // 引數: state - 當前狀態示例: data - fsm.reject(xxx) 執行傳遞過來的引數
      data.failList.forEach(fn => fn())
    }
  }
})
// 定義Promise
class MyPromise {
  constructor(fn) {
    this.successList = []
    this.failList = []

    fn(() => {
      // resolve函式
      fsm.resolve(this)
    }, () => {
      // reject 函式
      fsm.reject(this)
    })
  }
  then(successFn, failFn) {
    this.successList.push(successFn)
    this.failList.push(failFn)
  }
}

// test
function loadImg(src) {
  const promise = new MyPromise(function(resolve, reject) {
    let img = document.createElement('img')
    img.onload = function () {
      resolve(img)
    }
    img.onerror = function () {
      reject()
    }
    img.src = src
  })
  return promise
}

let src = "xxx.png"
let result = loadImg(src)
result.then(function () {
  console.log('ok1')
}, function () {
  console.log('fail1')
})

result.then(function () {
  console.log('ok2')
}, function () {
  console.log('fail2')
})

設計原則驗證

  • 將狀態物件和主題物件分離,狀態的變化邏輯單獨處理
  • 符合開放封閉原則

相關文章