1. 程式人生 > 實用技巧 >Javascript-設計模式_狀態模式

Javascript-設計模式_狀態模式

狀態模式

前言

狀態模式的關鍵是區分事物內部的狀態,事物內部狀態的改變往往會帶來事物的行為改變,即物件行為是基於狀態來改變的,內部的狀態轉化,導致了行為表現形式不同

當電燈開著,此時按下開關,電燈會切換到關閉狀態;再按一次開關,電燈又將被開啟。同一個開關在不同的狀態下,表現出來的行為是不一樣的

場景條件--有限狀態機

  • 狀態總數(state)是有限的

  • 任一時刻,只處在一種狀態之中

  • 某種條件下,會從一種狀態轉變(transition)到另一種狀態

允許一個物件在其內部狀態改變時改變它的行為,物件看起來似乎修改了它的類

解釋:

  • 將狀態封裝成獨立的類,並將請求委託給當前的狀態物件,當物件的內部狀態發生改變時,會帶來不同的行為變化

  • 使用的物件,在不同的狀態下具有截然不同的行為(委託效果)

談到封裝,一般優先考慮封裝物件的行為,而不是物件的狀態,但在狀態模式中剛好相反,狀態模式的關鍵是把事物的每種狀態都封裝成單獨的類

示例

點燈程式 (弱光 –> 強光 –> 關燈)迴圈

/* 關燈 */
const OffLightState = light => {
    this.light = light
}

/* 弱光 */
const WeakLightState = light => {
    this.light = light
}

/* 強光 */
const StrongLightState = light => {
    this.light = light
}

const Light = () => {
    // 開關狀態
    this.offLight = new OffLightState(this)
    this.weakLight = new WeakLightState(this)
    this.strongLight = new StrongLightState(this)
    // 快關按鈕
    this.button = null
}

Light.prototype.init = () => {
    const button = document.createElement('button')
    const self = this
    this.button = document.body.appendChild(button)
    this.button.innerHTML = '開關'
    this.currentState = this.offLight
    this.button.click = () => {
        self.currentState.buttonWasPressed()
    }
}

/* 讓抽象父類的抽象方法直接丟擲一個異常(避免狀態子類未實現buttonWasPressed方法) */
Light.prototype.buttonWasPressed = () => {
    throw new Error('父類的buttonWasPressed方法必須被重寫')
}

Light.prototype.setState = newState => {
    this.currentState = newState
}

/* 關燈 */
OffLightState.prototype = new Light()  // 繼承抽象類
OffLightState.prototype.buttonWasPressed = () => {
    console.log('關燈!')
    this.light.setState(this.light.weakLight)
}

/* 弱光 */
WeakLightState.prototype = new Light()
WeakLightState.prototype.buttonWasPressed = () => {
    console.log('弱光!')
    this.light.setState(this.light.strongLight)
}

/* 強光 */
StrongLightState.prototype = new Light()
StrongLightState.prototype.buttonWasPressed = () => {
    console.log('強光!')
    this.light.setState(this.light.offLight)
}

效能優化點

  1. 如何管理狀態物件的建立和銷燬

    第一種僅當state物件被需要時才建立並隨後銷燬(state物件比較龐大,優先選擇)

    另一種是一開始就建立好所有的狀態物件,並且始終不銷燬它們(狀態改變頻繁)

  2. 利用享元模式共享一個state物件

優缺點

優點

封裝了轉化規則,對於大量分支語句,可以考慮使用狀態類進一步封裝。 每個狀態都是確定的,所以物件行為是可控的

缺點

狀態模式的關鍵是將事物的狀態都封裝成單獨的類,這個類的各種方法就是“此種狀態對應的表現行為”, 因此狀態類會增加程式開銷

總結

狀態模式的使用場景也特別明確,有如下兩點

  1. 一個物件的行為取決於它的狀態,並且它必須在執行時刻根據狀態改變它的行為

  2. 一個操作中含有大量的分支語句,而且這些分支語句依賴於該物件的狀態。狀態通常為一個或多個列舉常量的表示

簡而言之,當遇到很多同級 if-else或者 switch的時候,可以使用狀態模式來進行簡化