狀態模式
一、定義
狀態模式( State Pattern)也稱為狀態機模式( State Machine pattern),是允許對象在內部狀態發生改變時改變它的行為,對象看起來好像修改了它的類,屬於行為型模式。允許對象在內部狀態發生改變時改變它的行為,對象看起來好像修改了它的類狀態模式中類的行為是由狀態決定的,不同的狀態下有不同的行為。其意圖是讓一個對象在其內部改變的時候,其行為也隨之改變。狀態模式核心是狀態與行為綁定,不同的狀態對應不同的行為。
狀態模式主要包含三種角色:
- 環境類角色( Context):定義客戶端需要的接口,內部維護一個當前狀態實例,並負責具體狀態的切換
- 抽象狀態角色( State):定義該狀態下的行為,可以有一個或多個行為;
- 具體狀態角色( Concretestate):具體實現該狀態對應的行為,並且在需要的情況下進行狀態切換
二、狀態模式的案例
比如訂單狀態的變化,審核流程單子狀態的變化等等。在軟件開發過程中,對於某一項操作,可能存在不同的情況。通常處理方式就是使用 if.else或 switch. case條件語句進行枚舉。但是這種做法天然存在弊端:條件判斷語句過於臃腫,可讀性差,且不具備擴展性,維護難度也大,而如果轉換思維,將這些不同狀態獨立起來用各個不同的類進行表示,系統處於那種狀態,直接使用相應的狀態類對象進行處理,消除了 if. else, switch.ase等冗餘語句,代碼具有層次性且具備良好擴展力
狀態模式主要解決的就是當控制一個對象狀態的條件表達式過於複雜時得情況,通過把狀態的判斷邏輯轉移到表示不同狀態的一系列類中,可以把複雜的判斷邏輯簡單化。對象得行為依賴於它的狀態(屬性),並且會根據它的狀態改變而改變它的相關行為。狀態模式適用於以下場景:
- 行為隨狀態改變而改變的場景
- 一個操作中含有龐大的多分支結構,並且這些分支取決於對象的狀態
1. Context類
環境角色具有兩個職責,即處理本狀態必須完成的任務,及決定是否可以過渡到其它狀態。對於環境角色,有幾個不成文的約束:
- 即把狀態對象聲明為靜態常量,有幾個狀態對象就聲明幾個狀態常量
- 環境角色具有狀態抽象角色定義的所有行為,具體執行使用委託方式
public class Context { //定義狀態 public final static State STATE1 = new ConcreteState1(); public final static State STATE2 = new ConcreteState2(); //當前狀態 private State currentState; //獲得當前狀態 public State getCurrentState() { return currentState; } //設置當前狀態 public void setCurrentState(State currentState) { this.currentState = currentState; // System.out.println("當前狀態:" + currentState); //切換狀態 this.currentState.setContext(this); } public void handle1() { this.currentState.handle1(); } public void handle2() { this.currentState.handle2(); } }
2. State抽象狀態類
抽象環境中聲明一個環境角色,提供各個狀態類自行訪問,並且提供所有狀態的抽象行為,由各個實現類實現。
public abstract class State { protected Context context; public void setContext(Context context) { this.context = context; } //行為1 public abstract void handle1(); //行為2 public abstract void handle2(); }
3. 具體狀態
具體狀態實現,這裡以定義ConcreteState1和ConcreteState2兩個具體狀態類為例,ConcreteState2的具體內容同ConcreteState1。
public class ConcreteState1 extends State { @Override public void handle1() { //... System.out.println("ConcreteState1 的 handle1 方法"); } @Override public void handle2() { super.context.setCurrentState(Context.STATE2); System.out.println("ConcreteState1 的 handle2 方法"); } }
public class ConcreteState2 extends State { @Override public void handle1() { //... System.out.println("ConcreteState2 的 handle2 方法"); } @Override public void handle2() { super.context.setCurrentState(Context.STATE1); System.out.println("ConcreteState2的 handle2 方法"); } }
4. Client客戶端
定義Context環境角色,初始化具體狀態1,執行行為觀察結果。
public class Client { public static void main(String[] args) { //定義環境角色 Context context = new Context(); //初始化狀態 context.setCurrentState(new ConcreteState1()); //行為執行 context.handle1(); context.handle2(); context.handle1(); context.handle2(); } }
從運行結果可見,我們已經隱藏了狀態的變化過程,它的切換引起了行為的變化。對外來說,我們只看到了行為的改變,而不用知道是狀態變化引起的。
三、總結
狀態模式與責任鏈模式:
狀態模式和責任鏈模式都能消除if分支過多的問題。但某些情況下,狀態模式中的狀態可以理解為責任,那麼這種情況下,兩種模式都可以使用從定義來看,狀態模式強調的是一個對象內在狀態的改變,而責任鏈模式強調的是外部節點對象間的改變。從其代碼實現上來看,他們間最大的區別就是狀態模式各個狀態對象知道自己下一個要進入的狀態對象;而責任鏈模式並不清楚其下一個節點處理對象,因為鏈式組裝由客戶端負責。
狀態模式與策略模式:
狀態模式和策略模式的UML類圖架構幾乎完全一樣, 但他們的應用場景是不一樣的。策略模式多種算法行為擇其一都能滿足,彼此之間是獨立的,用戶可自行更換策略算法;而狀態模式各個狀態間是存在相互關係的,彼此之間在一定條件下存在自動切換狀態效果,且用戶無法指定狀態,只能設置初始狀態。
優點:
結構清晰:將狀態獨立為類, 消除了冗餘的if…else或switch…case語句, 使代碼更加簡潔,提高系統可維護性;
將狀態轉換顯示化:通常的對象內部都是使用數值類型來定義狀態,狀態的切換是通過賦值進行表現,不夠直觀;而使用狀態類,在切換狀態時,是以不同的類進行表示,轉換目的更加明確;狀態類職責明確且具備擴展性。
缺點:
類膨脹:如果一個事物具備很多狀態,則會造成狀態類太多;
狀態模式的結構與實現都較為複雜,如果使用不當將導致程序結構和代碼的混亂;
狀態模式對開閉原則的支持並不太好,對於可以切換狀態的狀態模式,增加新的狀態類需要修改那些負責狀態轉換的源代碼,否則無法切換到新增狀態,而且修改某個狀態類的行為也需
git:源碼://github.com/ljx958720/design_patterns.git