目錄
狀态模式的定義
狀态模式的一個模型——有限狀态機 Finite-state machine
示範範例1 —— 狀态模式實作對話框的顯示和隐藏
示範範例2 —— 狀态模式實作角色扮演遊戲(如魂鬥羅)
示範範例3 —— 狀态模式實作紅綠燈
狀态模式的定義
狀态模式:當一個對象的内部狀态發生改變時,會導緻其行為的改變,這看起來像是改變了對象。
類型:對象行為型模式
用途:解決系統中複雜對象的狀态轉換以及不同狀态下行為的封裝問題
使用場景:
- 一個由一個或多個動态變化的屬性導緻發生不同行為的對象,在與外部事件産生互動時,其内部狀态就會改變,進而使得系統的行為也随之發生變化,那麼這個對象,就是有狀态的對象
- 代碼中包含大量與對象狀态有關的條件語句,像是if else或switch case語句,且這些條件執行與否依賴于該對象的狀态。
優點:
- 一個狀态狀态對應行為,封裝在一個類裡,更直覺清晰,增改友善
- 狀态與狀态間,行為與行為間彼此獨立互不幹擾
- 避免事物對象本身不斷膨脹,條件判斷語句過多
- 每次執行動作不用走很多不必要的判斷語句,用哪個拿哪個
缺點:
- 需要将事物的不同狀态以及對應的行為拆分出來,有時候會無法避免的拆分的很細,有的時候涉及業務邏輯,一個動作拆分出對應的兩個狀态,動作就拆不明白了,過度設計
- 必然會增加事物類和動作類的個數,有時候動作類再根據單一原則,按照功能拆成幾個類,會反而使得代碼混亂,可讀性降低
狀态模式的一個模型——有限狀态機 Finite-state machine
日常開發中很多具有多種狀态的對象,都可以用有限狀态機模型來模拟,一般都具有以下特點:
- 事物擁有多種狀态,任一時間隻會處于一種狀态不會處于多種狀态;
- 動作可以改變事物狀态,一個動作可以通過條件判斷,改變事物到不同的狀态,但是不能同時指向多個狀态,一個時間,就一個狀态
- 狀态總數是有限的;
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的實作。