狀態模式又稱對象模式,狀態模式是對象的行爲模式。ide
狀態模式容許一個對象在其內部狀態改變的時候改變其行爲。這個對象看上去就像是改變了它的類同樣。this
用一句話來講,狀態模式把所研究的對象的行爲包裝在不一樣的狀態對象裏,每個狀態對象都屬於一個抽象狀態類的子類。狀態模式的意圖是讓一個對象在其內部狀態改變的時候,其行爲也隨之改變。spa
結構以下:3d
涉及到的角色以下:code
環境(Context)角色,也成上下文:定義客戶端所感興趣的接口,而且保留一個具體狀態類的實例。這個具體狀態類的實例給出此環境對象的現有狀態。
抽象狀態(State)角色:定義一個接口,用以封裝環境(Context)對象的一個特定的狀態所對應的行爲。
具體狀態(ConcreteState)角色:每個具體狀態類都實現了環境(Context)的一個狀態所對應的行爲。對象
源代碼以下:blog
環境角色:接口
public class Context { private State state; public void setState(State state) { this.state = state; state.handleState(); } public State getState() { return state; } }
抽象狀態:生命週期
/** * 狀態類 * * @author Administrator * */ public interface State { void handleState(); }
具體狀態A:get
public class ConcreteSateA implements State { @Override public void handleState() { System.out.println("A狀態處理請求"); } }
具體狀態B:
public class ConcreteSateB implements State { @Override public void handleState() { System.out.println("B狀態處理請求"); } }
客戶端:
public class Client { public static void main(String[] args) { Context context = new Context(); State state = new ConcreteSateA(); context.setState(state); State state2 = new ConcreteSateB(); context.setState(state2); } }
結果:
A狀態處理請求
B狀態處理請求
注意:上面狀態的變化是由客戶端來決定的,也能夠由具體狀態決定,也就是由ConcreteState自行決定它的下一任狀態。
路口的交通訊號燈就有不一樣的狀態,紅燈停、綠燈行、黃燈暫停。並且狀態是固定變化的,紅->綠->藍。假設紅燈亮30s,綠燈30s,黃燈3s。
結構以下:
源代碼以下:
環境角色:
public class Context { private LightState state; public void setState(LightState state) { this.state = state; } // 也能夠將此方法直接寫到上面setState方法中 void action() { state.action(this); } }
抽象狀態:傳context是爲了在具體狀態類中改變context狀態
/** * 狀態類 * * @author Administrator * */ public interface LightState { void action(Context context); }
具體狀態類:
public class RedLightState implements LightState { @Override public void action(Context context) { System.out.println("紅燈停"); // 模擬亮30秒鐘 try { Thread.sleep(30 * 1000); } catch (InterruptedException ignored) { // ignore } // 改成綠燈 context.setState(new GreenLightState()); context.action(); } }
public class GreenLightState implements LightState { @Override public void action(Context context) { System.out.println("綠燈行"); // 模擬亮30秒鐘 try { Thread.sleep(30 * 1000); } catch (InterruptedException ignored) { // ignore } // 改成黃燈 context.setState(new YellowLightState()); context.action(); } }
public class YellowLightState implements LightState { @Override public void action(Context context) { System.out.println("黃燈暫停"); // 模擬亮3秒鐘 try { Thread.sleep(3 * 1000); } catch (InterruptedException ignored) { // ignore } // 改成紅燈 context.setState(new RedLightState()); context.action(); } }
客戶端:
public class Client { public static void main(String[] args) { Context context = new Context(); context.setState(new GreenLightState()); context.action(); } }
結果:
綠燈行
黃燈暫停
紅燈停
綠燈行
...
意圖:容許對象在內部狀態發生改變時改變它的行爲,對象看起來好像修改了它的類。
主要解決:對象的行爲依賴於它的狀態(屬性),而且能夠根據它的狀態改變而改變它的相關行爲。
什麼時候使用:代碼中包含大量與對象狀態有關的條件語句。
如何解決:將各類具體的狀態類抽象出來。
關鍵代碼:一般命令模式的接口中只有一個方法。而狀態模式的接口中有一個或者多個方法。並且,狀態模式的實現類的方法,通常返回值,或者是改變實例變量的值。也就是說,狀態模式通常和對象的狀態有關。實現類的方法有不一樣的功能,覆蓋接口中的方法。狀態模式和命令模式同樣,也能夠用於消除 if...else 等條件選擇語句。
應用實例: 一、打籃球的時候運動員能夠有正常狀態、不正常狀態和超常狀態。 二、曾侯乙編鐘中,'鍾是抽象接口','鍾A'等是具體狀態,'曾侯乙編鐘'是具體環境(Context)。
優勢: 一、封裝了轉換規則。 二、枚舉可能的狀態,在枚舉狀態以前須要肯定狀態種類。 三、將全部與某個狀態有關的行爲放到一個類中,而且能夠方便地增長新的狀態,只須要改變對象狀態便可改變對象的行爲。 四、容許狀態轉換邏輯與狀態對象合成一體,而不是某一個巨大的條件語句塊。 五、可讓多個環境對象共享一個狀態對象,從而減小系統中對象的個數。
缺點: 一、狀態模式的使用必然會增長系統類和對象的個數。 二、狀態模式的結構與實現都較爲複雜,若是使用不當將致使程序結構和代碼的混亂。 三、狀態模式對"開閉原則"的支持並不太好,對於能夠切換狀態的狀態模式,增長新的狀態類須要修改那些負責狀態轉換的源代碼,不然沒法切換到新增狀態,並且修改某個狀態類的行爲也需修改對應類的源代碼。
使用場景: 一、行爲隨狀態改變而改變的場景。 二、條件、分支語句的代替者。
注意事項:在行爲受狀態約束的時候使用狀態模式,並且狀態不超過 5 個。
狀態模式與策略模式區別:
若是環境角色只有一個狀態應該使用策略模式。
策略模式的特色是一旦角色選擇了一個具體策略類,那麼在整個環境類的生命週期裏它都不會改變這個具體策略類。而狀態模式則適用於另外一種狀況,即環境角色有明顯的狀態轉移,在環境的生命週期裏有幾個不一樣的狀態對象被使用。
另外一個微妙的區別在於策略模式的環境類本身選擇一個具體策略類,而狀態模式的環境類是被外在緣由放進一個具體狀態中。
策略模式所選的策略每每並不明顯地告訴客戶端它所選擇的具體策略;而狀態模式則相反,環境角色所處的狀態是明顯告訴客戶端的。