《JavaScript設計模式與開發實踐》模式篇(13)—— 狀態模式

狀態模式的關鍵是區分事物內部的狀態,事物內部狀態的改變每每會帶來事物的行爲改變。設計模式

故事背景

咱們來想象這樣一個場景:有一個電燈,電燈上面只有一個開關。當電燈開着的時候,此時 按下開關,電燈會切換到關閉狀態;再按一次開關,電燈又將被打開。同一個開關按鈕,在不一樣 的狀態下,表現出來的行爲是不同的bash

代碼實現(未使用狀態模式)

var Light = function(){ 
    this.state = 'off'; // 給電燈設置初始狀態 off
    this.button = null;// 電燈開關按鈕
};
Light.prototype.init = function(){
    var button = document.createElement( 'button' ),
    self = this;
    button.innerHTML = '開關';
    this.button = document.body.appendChild( button ); 
    this.button.onclick = function(){
        self.buttonWasPressed(); 
    }
};
Light.prototype.buttonWasPressed = function(){ 
    if ( this.state === 'off' ){
        console.log( '開燈' );
        this.state = 'on';
    } else if ( this.state === 'on' ){
        console.log( '關燈' );
        this.state = 'off'; 
    }
};
var light = new Light(); 
light.init();
複製代碼

存在的問題

假如如今電燈的狀態多了一種,第一次按下打開弱光,第二次按下打開強光,第三次纔是關閉電燈。以上的代碼就沒法知足這種電燈的狀況app

分析

狀態模式的關鍵是把事物的 每種狀態都封裝成單獨的類,跟此種狀態有關的行爲都被封裝在這個類的內部,因此 button 被按下的的時候,只須要在上下文中,把這個請求委託給當前的狀態對象便可,該狀態對象會負責渲染它自身的行爲。以下圖所示post

重構思路

  • 定義 3 個狀態類
  • 改寫 Light 類,使用狀態對象記錄當前的狀態
  • 提供一個 方法來切換 light 對象的狀態
/******************** 定義 3 個狀態類 ************************/
// OffLightState:
var OffLightState = function( light ){ 
    this.light = light;
};
OffLightState.prototype.buttonWasPressed = function(){ 
    console.log( '弱光' ); // offLightState 對應的行爲 
    this.light.setState( this.light.weakLightState );// 切換狀態到 weakLightState
};
// WeakLightState:
var WeakLightState = function( light ){ 
    this.light = light;
};
WeakLightState.prototype.buttonWasPressed = function(){ 
    console.log( '強光' ); // weakLightState 對應的行爲 
    this.light.setState( this.light.strongLightState ); //切換狀態到 strongLightState
};
// StrongLightState:
var StrongLightState = function( light ){ 
    this.light = light;
};
StrongLightState.prototype.buttonWasPressed = function(){
    console.log( '關燈' ); // strongLightState 對應的行爲
    this.light.setState( this.light.offLightState ); // 切換狀態到 offLightState
};
/******************* 改寫 Light 類,使用狀態對象記錄當前的狀態 ******************/
var Light = function(){
    this.offLightState = new OffLightState( this ); 
    this.weakLightState = new WeakLightState( this ); 
    this.strongLightState = new StrongLightState( this ); 
    this.button = null;
};
/******************** 提供一個 方法來切換 light 對象的狀態 ************************/
Light.prototype.init = function(){
    var button = document.createElement( 'button' ),
    self = this;
    this.button = document.body.appendChild( button ); 
    this.button.innerHTML = '開關';
   this.currState = this.offLightState;
    this.button.onclick = function(){ 
        self.currState.buttonWasPressed();
    } 
};
Light.prototype.setState = function( newState ){
    this.currState = newState; 
};
var light = new Light(); 
light.init();
複製代碼

改進效果

執行結果跟以前的代碼一致,可是使用狀態模式的好處很明顯,它可使每 一種狀態和它對應的行爲之間的關係局部化,這些行爲被分散和封裝在各自對應的狀態類之中, 便於閱讀和管理代碼。 另外,狀態之間的切換都被分佈在狀態類內部,這使得咱們無需編寫過多的 if、else 條件 分支語言來控制狀態之間的轉換。this

總結

  • 狀態模式定義了狀態與行爲之間的關係,並將它們封裝在一個類裏。經過增長新的狀態 類,很容易增長新的狀態和轉換。
  • 避免 Context 無限膨脹,狀態切換的邏輯被分佈在狀態類中,也去掉了 Context 中本來過 5 多的條件分支。
  • 用對象代替字符串來記錄當前狀態,使得狀態的切換更加一目瞭然。
  • Context 中的請求動做和狀態類中封裝的行爲能夠很是容易地獨立變化而互不影響

系列文章:

《JavaScript設計模式與開發實踐》最全知識點彙總大全spa

相關文章
相關標籤/搜索