顧名思義,狀態模式用於解決系統中與狀態相關的問題,如複雜對象的狀態轉換以及不同狀態下行為的封裝問題。 ...
簡介
狀態模式(State Design Pattern)的定義是,允許一個對象在內部狀態改變時改變它的行為,對象看起來似乎修改了它的類。
在狀態模式中,通常有兩種方式實現狀態轉換:統一由環境類來負責狀態之間的轉換;由具體狀態類來負責狀態之間的轉換。
狀態機
概念
狀態模式一般用於實現狀態機,而狀態機常用在游戲、工作流引擎等系統開發中。狀態機的實現方式有多種,除了狀態模式,比較常用的還有分支邏輯法和查表法。
狀態機會有 3 個組成部分:狀態(State)、事件(Event)、動作(Action)。
拿“超級馬利奧”游戲來舉例,其中馬利奧形態的轉變就是一個狀態機:初始狀態是小馬利奧,吃蘑菇這個事件會觸髮狀態的轉移,從小馬利奧轉變成超級馬利奧,以及觸發動作的執行(增加積分)。
分支邏輯法
最簡單的狀態機實現方式就是分支邏輯法,其理解非常簡單,就是將每一個狀態轉移都直譯成代碼。
其缺點是,代碼中會充斥著 if-else 或 switch 分支判斷邏輯,甚至是嵌套的分支判斷邏輯,當狀態較多時,代碼的可讀性會比較低。
查表法
查表法的實現邏輯是,將狀態、事件和動作三者存儲到一個二維表中,這樣可以清晰地表示,一個動作發生某個事件時,會轉移到怎樣的狀態以及觸發怎樣的動作。
在實現過程中,將二維表的數據存儲到配置文件中,可以通過動態地修改配置文件以達到修改狀態機的目的。
具體實現
仍然還是拿“超級馬利奧”游戲來舉例說明,初始狀態是小馬利奧,吃蘑菇這個事件會觸髮狀態的轉移,從小馬利奧轉變成超級馬利奧,以及觸發動作的執行(增加積分)。
首先,定義一個抽象狀態 State
介面,其代碼示例如下:
public interface State {
// 聲明抽象業務方法,不同的具體狀態可以有不同的方法實現
void handle();
}
對於小馬利奧狀態,定義一個實現 State
介面的 SmallState
類,其代碼示例如下:
public class SmallState implements State {
@Override
public void handle() {
// 業務方法的具體實現
System.out.println("變成小馬利奧狀態");
}
}
對於超級馬利奧狀態,定義一個實現 State
介面的 SuperState
類,其代碼示例如下:
public class LargeState implements State {
@Override
public void handle() {
// 業務方法的具體實現
System.out.println("變成超級馬利奧狀態");
}
}
在狀態模式中,需要創建一個 Context
類用於保存對於一個具體狀態對象的引用,並且負責狀態的保持和轉變。其代碼示例如下:
public class Context {
private State state;
public void setState(State state) {
// 註入狀態對象
this.state = state;
}
public void request() {
// 調用狀態對象的業務方法
this.state.handle();
}
}
對於客戶端,直接操作 Context
對象並根據狀態的轉變傳入不同的狀態對象,這樣即可實現狀態機的功能,其代碼示例如下:
class StateDemo {
public static void main(String[] args) {
Context context = new Context();
State smallState = new SmallState();
context.setState(smallState);
// 變成小馬利奧狀態
context.request();
State largeState = new LargeState();
context.setState(largeState);
// 變成超級馬利奧狀態
context.request();
}
}
總結
優點
狀態模式的主要優點如下:
- 狀態模式統一封裝了狀態的轉換規則,對狀態轉換代碼進行集中管理
- 將不同的狀態引入獨立的對象中使得狀態轉換變得更加明確,且減少對象間的相互依賴
- 狀態的職責分明,通過定義新的子類可以很容易地增加新的狀態和轉換
缺點
狀態模式的主要缺點如下:
- 每個狀態都會新增一個具體的狀態子類,導致系統的運行開銷增大
- 狀態模式的結構和實現都較為複雜,使用不當會導致程式結構和代碼的混亂
- 對於可以切換的狀態模式,增加新的狀態類需要修改那些負責狀態轉換的源碼,否則無法切換到新增的狀態,而且修改某個狀態類的行為也要修改對應類的源碼
適用場景
狀態模式的適用場景如下:
- 對象的行為依賴於它的狀態,狀態的改變將導致行為的變化
- 在代碼中包括大量與對象狀態有關的條件語句