天天看點

設計模式19 - 狀态模式【State Pattern】

狀态模式

定義:

當一個對象的内在狀态改變時允許改變其行為,這個對象看起來像是改變了其類。

​狀态模式主要解決的是當控制一個對象狀态的條件表達式過于複雜時的情況。把狀态的判斷邏輯轉移到表示不同狀态的一系列類中,可以把複雜的判斷邏輯簡化。​

​舉例(電梯例子,代碼是最終結果):​

分析:電梯類裡面過多的判斷狀态,導緻類十分龐大,是以分到不同狀态的一系列類中去。

/**
* 定義一個電梯的接口
*/
public abstract class LiftState{ 
    //定義一個環境角色,也就是封裝狀态的變換引起的功能變化
    protected Context context; 

    public void setContext(Context _context){ 
         this.context = _context; 
    } 

    //首先電梯門開啟動作
    public abstract void open(); 

    //電梯門有開啟,那當然也就有關閉了
    public abstract void close(); 

    //電梯要能上能下,跑起來
    public abstract void run(); 

    //電梯還要能停下來,停不下來那就扯淡了
    public abstract void stop(); 
} 


public class Context { 
    //定義出所有的電梯狀态
    public final static OpenningState openningState = new OpenningState(); 
    public final static ClosingState closeingState = new ClosingState(); 
    public final static RunningState runningState = new RunningState(); 
    public final static StoppingState stoppingState = new StoppingState(); 

    //定一個目前電梯狀态
    private LiftState liftState; 

    public LiftState getLiftState() { 
         return liftState; 
    } 

    public void setLiftState(LiftState liftState) { 
         this.liftState = liftState; 
         //把目前的環境通知到各個實作類中
         this.liftState.setContext(this); 
     } 

    public void open(){ 
         this.liftState.open(); 
     } 

    public void close(){ 
         this.liftState.close(); 
     } 

    public void run(){ 
         this.liftState.run(); 
     } 

    public void stop(){ 
         this.liftState.stop(); 
     } 
} 


/**
* 在電梯門開啟的狀态下能做什麼事情
*/
public class OpenningState extends LiftState { 
    //開啟當然可以關閉了,我就想測試一下電梯門開關功能
    @Override
    public void close() { 
     //狀态修改
     super.context.setLiftState(Context.closeingState); 
     //動作委托為CloseState來執行
     super.context.getLiftState().close(); 
 } 

    //打開電梯門
    @Override
    public void open() { 
         System.out.println("電梯門開啟..."); 
    } 

    //門開着電梯就想跑,這電梯,吓死你!
    @Override
    public void run() { 
         //do nothing;
     } 

    //開門還不停止?
    public void stop() { 
         //do nothing;
    } 
} 


/**
* 電梯門關閉以後,電梯可以做哪些事情
*/
public class ClosingState extends LiftState { 

    //電梯門關閉,這是關閉狀态要實作的動作
    @Override
    public void close() { 
         System.out.println("電梯門關閉..."); 
     } 

    //電梯門關了再打開,逗你玩呢,那這個允許呀
    @Override
    public void open() { 
         super.context.setLiftState(Context.openningState); //置為門敞狀态
         super.context.getLiftState().open(); 
     } 

    //電梯門關了就跑,這是再正常不過了
    @Override
    public void run() { 
         super.context.setLiftState(Context.runningState); //設定為運作狀态;
         super.context.getLiftState().run(); 
     } 

    //電梯門關着,我就不按樓層
    @Override
    public void stop() { 
         super.context.setLiftState(Context.stoppingState); //設定為停止狀态;
         super.context.getLiftState().stop(); 
     } 
} 

/**
* 電梯在運作狀态下能做哪些動作
*/
public class RunningState extends LiftState { 

    //電梯門關閉?這是肯定了
    @Override
    public void close() { 
         //do nothing
    } 

    //運作的時候開電梯門?你瘋了!電梯不會給你開的
    @Override
    public void open() { 
         //do nothing
     } 

    //這是在運作狀态下要實作的方法
    public void run() { 
         System.out.println("電梯上下跑..."); 
     } 

    //這個事絕對是合理的,光運作不停止還有誰敢做這個電梯?!估計隻有上帝了
    @Override
    public void stop() { 
         super.context.setLiftState(Context.stoppingState); //環境設定為停止狀态;
         super.context.getLiftState().stop(); 
    } 
} 


/**
* 在停止狀态下能做什麼事情
*/
public class StoppingState extends LiftState { 

    //停止狀态關門?電梯門本來就是關着的!
    @Override
    public void close() { 
      //do nothing;
    } 

    //停止狀态,開門,那是要的
    @Override
    public void open() { 
      super.context.setLiftState(Context.openningState); 
      super.context.getLiftState().open(); 
   } 

    //停止狀态再跑起來,正常的很
    @Override
    public void run() { 
      super.context.setLiftState(Context.runningState); 
      super.context.getLiftState().run(); 
      } 

    //停止狀态是怎麼發生的呢?當然是停止方法執行了
    @Override
    public void stop() { 
          System.out.println("電梯停止了..."); 
    } 
}  

public class Client { 

    public static void main(String[] args) { 
         Context context = new Context(); 
         context.setLiftState(new ClosingState()); 

         context.open(); 
         context.close(); 
         context.run(); 
         context.stop(); 
     } 
}      

​總結:​

​意圖:​允許對象在内部狀态發生改變時改變它的行為,對象看起來好像修改了它的類。

​主要解決:​對象的行為依賴于它的狀态(屬性),并且可以根據它的狀态改變而改變它的相關行為。

​何時使用:​代碼中包含大量與對象狀态有關的條件語句。

​如何解決:​将各種具體的狀态類抽象出來。

​關鍵代碼:​通常指令模式的接口中隻有一個方法。而狀态模式的接口中有一個或者多個方法。而且,狀态模式的實作類的方法,一般傳回值,或者是改變執行個體變量的值。也就是說,狀态模式一般和對象的狀态有關。實作類的方法有不同的功能,覆寫接口中的方法。狀态模式和指令模式一樣,也可以用于消除 if…else 等條件選擇語句。

​優點:​

1、封裝了轉換規則。

2、枚舉可能的狀态,在枚舉狀态之前需要确定狀态種類。

3、将所有與某個狀态有關的行為放到一個類中,并且可以友善地增加新的狀态,隻需要改變對象狀态即可改變對象的行為。

4、允許狀态轉換邏輯與狀态對象合成一體,而不是某一個巨大的條件語句塊。

5、可以讓多個環境對象共享一個狀态對象,進而減少系統中對象的個數。

​缺點:​

1、狀态模式的使用必然會增加系統類和對象的個數。

3、狀态模式對”開閉原則”的支援并不太好,對于可以切換狀态的狀态模式,增加新的狀态類需要修改那些負責狀态轉換的源代碼,否則無法切換到新增狀态,而且修改某個狀态類的行為也需修改對應類的源代碼。

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

​注意事項:​在行為受狀态限制的時候使用狀态模式,而且狀态不超過 5 個。