定義
允許一個對象在其內部狀態改變時改變它的行為,對象看起來似乎修改了它的類。
要點
- 狀態模式的關鍵是區分事物內部的狀態,事物內部狀態的改變往往會帶來事物的行為改變。把事物的每種狀態都封裝成單獨的類,跟此種狀態有關的行為都封裝在這個類的內部。
- 在主體類(Context)的構造函數裡為每個狀態類都創建一個狀態對象。主體對象不再執行任何實質性的操作,而是把請求委託給當前所處的狀態的對象去執行。
- 狀態模式可以避免 Context 無限膨脹,狀態切換的邏輯被分佈在狀態類中,去掉了 Context 中原本過多的條件分支。
- 缺少抽象類的變通方式:讓父類的方法直接拋出一個異常,這個異常會在程序運行期間被發現。
- 狀態模式的缺點是邏輯分散在狀態類中,雖然避開了不受歡迎的條件分支語句,但也造成了邏輯分散的問題,我們無法在一個地方就看出整個狀態機的邏輯。
- 狀態模式和策略模式:它們都有一個上下文、一些狀態或者策略類,上下文把請求委託給這些類來執行。它們的區別是策略模式中的各個策略類是平行的,互相之間沒有聯繫,用戶使用前需要知道所有策略類的作用;狀態模式中,狀態對應的行為以及狀態之間的切換是已經規定好的,「改變行為」只發生在狀態模式內部,用戶是不需要瞭解這些細節的。
- JavaScript 中可以通過 Function.prototype.call 方式非常方便地使用委託技術,並不需要事先讓一個對象持有另一個對象的引用。
- 表驅動的有限狀態機
核心代碼
var Light = function() {
this.currState = FSM.off;
this.button = null;
};
Light.prototype.init = function() {
var self = this;
var button = document.createElement('button');
this.button = button;
// ...
this.button.onclick = function() {
self.currState.buttonWasPressed.call(self);
};
};
var FSM = {
off: {
buttonWasPressed: function() {
// ...
this.currState = FSM.on;
}
},
on: {
buttonWasPressed: function() {
// ...
this.currState = FSM.off;
}
}
};
var delegate = function(client, delegation) {
return {
buttonWasPressed: function() {
return delegation.buttonWasPressed.apply(client, arguments);
}
}
};
var Light = function() {
this.button = null;
this.offState = delegate(this, FSM.off);
this.onState = delegate(this, FSM.on);
this.currState = this.offState;
};
Light.prototype.init = function() {
var self = this;
// ...
this.button.onclick = function() {
self.currState.buttonWasPressed();
};
};