天天看點

js設計模式【詳解】—— 狀态模式

目錄

​​狀态模式的定義​​

​​狀态模式的一個模型——有限狀态機 Finite-state machine​​

​​示範範例1 —— 狀态模式實作對話框的顯示和隐藏​​

​​示範範例2 —— 狀态模式實作角色扮演遊戲(如魂鬥羅)​​

​​示範範例3 —— 狀态模式實作紅綠燈​​

狀态模式的定義

狀态模式:當一個對象的内部狀态發生改變時,會導緻其行為的改變,這看起來像是改變了對象。

類型:對象行為型模式

用途:解決系統中複雜對象的狀态轉換以及不同狀态下行為的封裝問題

使用場景:

  1. 一個由一個或多個動态變化的屬性導緻發生不同行為的對象,在與外部事件産生互動時,其内部狀态就會改變,進而使得系統的行為也随之發生變化,那麼這個對象,就是有狀态的對象
  2. 代碼中包含大量與對象狀态有關的條件語句,像是if else或switch case語句,且這些條件執行與否依賴于該對象的狀态。

優點:

  1. 一個狀态狀态對應行為,封裝在一個類裡,更直覺清晰,增改友善
  2. 狀态與狀态間,行為與行為間彼此獨立互不幹擾
  3. 避免事物對象本身不斷膨脹,條件判斷語句過多
  4. 每次執行動作不用走很多不必要的判斷語句,用哪個拿哪個

缺點:

  1. 需要将事物的不同狀态以及對應的行為拆分出來,有時候會無法避免的拆分的很細,有的時候涉及業務邏輯,一個動作拆分出對應的兩個狀态,動作就拆不明白了,過度設計
  2. 必然會增加事物類和動作類的個數,有時候動作類再根據單一原則,按照功能拆成幾個類,會反而使得代碼混亂,可讀性降低

狀态模式的一個模型——有限狀态機 Finite-state machine

 日常開發中很多具有多種狀态的對象,都可以用有限狀态機模型來模拟,一般都具有以下特點:

  1. 事物擁有多種狀态,任一時間隻會處于一種狀态不會處于多種狀态;
  2. 動作可以改變事物狀态,一個動作可以通過條件判斷,改變事物到不同的狀态,但是不能同時指向多個狀态,一個時間,就一個狀态
  3. 狀态總數是有限的;

github上有一個有限狀态機的函數庫javascript-state-machine,可以了解一下

​​https://github.com/jakesgordon/javascript-state-machine​​

示範範例1 —— 狀态模式實作對話框的顯示和隐藏

var Dialog = function(){
  var _state = null;
 
  this.setState = function(state){
    _state = state;
  }
  this.getState = function(){
    return _state;
  }
}
 
var ShowState = function(){
  this.doAction = function(dialog){
    console.log("對Dialog設定顯示狀态:");
    dialog.setState(this);
  }
  this.toString = function(){
    console.log("顯示中......");
  }
}
 
var HideState = function(){
  this.doAction = function(dialog){
    console.log("對Dialog設定隐藏狀态:");
    dialog.setState(this);
  }
  this.toString = function(){
    console.log("已隐藏......");
  }
}
 
var dialog = new Dialog();
 
var showState = new ShowState();
var hideState = new HideState();
 
showState.doAction(dialog);
//對Dialog設定顯示狀态:
dialog.getState().toString();
//顯示中......
hideState.doAction(dialog);
//對Dialog設定隐藏狀态
dialog.getState().toString();
//已隐藏......      

例子裡Dialog對象有兩種狀态,顯示和隐藏,我把兩種狀态提取出來,使得狀态的管理更加靈活。在這個例子裡面Dialog稱之為環境類,環境類又稱為上下文類,他擁有多種狀态。環境類内部需要維護一個state對象用來定義目前狀态。HideState,ShowState稱之為狀态類,對應環境類的一個具體狀态,toString稱之為狀态類的行為,每一個狀态類的行為都有所不同。

示範範例2 —— 狀态模式實作角色扮演遊戲(如魂鬥羅)

class Contra {
  constructor () {
    //存儲目前待執行的動作 們
    this._currentstate = {};
  }
  //添加動作
  changeState (){
    //清空目前的動作集合
    this._currentstate = {};
    //周遊添加動作
    Object.keys(arguments).forEach(
      (i) => this._currentstate[arguments[i]] = true
    )
    return this;
  }
  //執行動作
  contraGo (){
    //目前動作集合中的動作依次執行
    Object.keys(this._currentstate).forEach(
      (k) => Actions[k] && Actions[k].apply(this)
    )
    return this;
  }
};

const Actions = {
  up : function(){
    //向上跳
    console.log('up');
  },
  down : function(){
    //趴下
    console.log('down');
  },
  forward : function(){
    //向前跑
    console.log('forward');
  },
  backward : function(){
    //往老家跑
    console.log('backward');
  },
  shoot : function(){
    //開槍吧
    console.log('shoot');
  },
};
var littlered = new Contra();
littlered.changeState('shoot','up').contraGo();      

控制台會輸出: shoot up

狀态模式,将條件判斷的結果轉化為狀态對象内部的狀态(代碼中的up,down,backward,forward),内部狀态通常作為狀态對象内部的私有變量(this._currentState),然後提供一個能夠調用狀态對象内部狀态的接口方法對象(changeState,contraGo),這樣對狀态的改變,對狀态方法的調用的修改和增加也會很容易,友善了對狀态對象中内部狀态的管理。

同時,狀态模式将每一個條件分支放入一個獨立的類中,也就是代碼中的Actions。這使得你可以根據對象自身的情況将對象的狀态(動作——up,down,backward,forward)作為一個對象(Actions.up,Actions.down這樣),這一對象可以不依賴于其他對象而獨立變化(一個行為一個動作,互不幹擾)。

可以看出,狀态模式就是一種适合多種狀态場景下的設計模式,改寫之後代碼更加清晰,提高代碼的維護性和擴充性,不用再牽一發動全身

示範範例3 —— 狀态模式實作紅綠燈

var trafficLight = (function () {
  var currentLight = null;
  return {
    change: function (light) {
      currentLight = light;
      currentLight.go();
    }
  }
})();

function RedLight() { }
RedLight.prototype.go = function () {
  console.log("this is red light");
}
function GreenLight() { }
GreenLight.prototype.go = function () {
  console.log("this is green light");
}
function YellowLight() { }
YellowLight.prototype.go = function () {
  console.log("this is yellow light");
}

trafficLight.change(new RedLight());
trafficLight.change(new YellowLight());      

trafficLight是一個紅綠燈的執行個體,傳入一個構造函數,對象暴露change方法改變内部狀态,也就是燈的顔色,change接收的同樣是一個狀态的對象,調用對象的方法觸發響應的動作,這裡的動作都叫go,不同顔色的燈對象有着不同的go的實作。