狀態模式


在狀態模式(State Pattern)中,類別的行為是基於它的狀態改變的。這種類型的設計模式屬於行為型模式。

在狀態模式中,我們建立表示各種狀態的物件和一個行為隨著狀態物件改變而改變的 context 物件。

介紹

意圖:允許物件在內部狀態改變時改變它的行為,物件看起來好像修改了它的類別。

主要解決:物件的行為依賴它的狀態(屬性),並且可以根據它的狀態改變而改變它的相關行為。

何時使用:程式碼中包含大量與物件狀態相關的條件語句。

如何解決:將各種具體的狀態類別抽象化。

關鍵程式碼:通常命令模式的介面中只有一個方法。而狀態模式的介面中有一個或多個方法。而且,狀態模式的實作類別的方法,一般回傳值,或是改變實例變數的值。也就是說,狀態模式一般和物件的狀態有關。實作類別的方法有不同的功能,覆蓋介面中的方法。狀態模式和指令模式一樣,也可以用來消除 if...else 等條件選擇語句。

應用範例:1、打籃球的時候運動員可以有正常狀態、不正常狀態和超常狀態。 2.曾侯乙編鐘中,'鐘是抽象介面','鐘A'等是具體狀態,'曾侯乙編鐘'是具體環境(Context)。

優點:1、封裝了轉換規則。 2.枚舉可能的狀態,在枚舉狀態前需要確定狀態種類。 3.將所有與某個狀態有關的行為放到一個類別中,並且可以方便地增加新的狀態,只需要改變物件狀態即可改變物件的行為。 4.允許狀態轉換邏輯與狀態物件合成一體,而不是某一個巨大的條件語句塊。 5.可以讓多個環境對象共享一個狀態對象,進而減少系統中對象的數量。

缺點:1、狀態模式的使用必然會增加系統類別和物件的數量。 2.狀態模式的結構與實作都較為複雜,如果使用不當將導致程式結構與程式碼的混亂。 3.狀態模式對"開閉原則"的支援並不太好,對於可以切換狀態的狀態模式,增加新的狀態類別需要修改那些負責狀態轉換的源代碼,否則無法切換到新增狀態,而且修改某個狀態類別的行為也需修改對應類別的原始碼。

使用場景:1、行為隨狀態改變而改變的場景。 2、條件、分支語句的代替者。

注意事項:在行為受狀態約束的時候使用狀態模式,且狀態不超過 5 個。

實作

我們將建立一個 State 介面和實作了 State 介面的實體狀態類別。 Context 是一個帶有某個狀態的類別。

StatePatternDemo,我們的示範類別使用 Context 和狀態物件來示範 Context 在狀態改變時的行為變化。

state_pattern_uml_diagram.jpg

步驟 1

建立一個介面。

State.java

public interface State {
   public void doAction(Context context);
}

步驟 2

#建立實作介面的實體類別。

StartState.java

public class StartState implements State {

   public void doAction(Context context) {
      System.out.println("Player is in start state");
      context.setState(this);	
   }

   public String toString(){
      return "Start State";
   }
}

StopState.java

public class StopState implements State {

   public void doAction(Context context) {
      System.out.println("Player is in stop state");
      context.setState(this);	
   }

   public String toString(){
      return "Stop State";
   }
}

步驟3

建立 Context 類別。

Context.java

public class Context {
   private State state;

   public Context(){
      state = null;
   }

   public void setState(State state){
      this.state = state;		
   }

   public State getState(){
      return state;
   }
}

步驟4

#使用Context 來查看當狀態State 改變時的行為變化。

StatePatternDemo.java

public class StatePatternDemo {
   public static void main(String[] args) {
      Context context = new Context();

      StartState startState = new StartState();
      startState.doAction(context);

      System.out.println(context.getState().toString());

      StopState stopState = new StopState();
      stopState.doAction(context);

      System.out.println(context.getState().toString());
   }
}

步驟 5

驗證輸出。

Player is in start state
Start State
Player is in stop state
Stop State