狀態模式 State,狀態模式通過將複雜的條件判斷轉換為狀態模式,不同條件下的行為轉換為狀態對象,進而呈現出不同的行為,本文對狀態模式進行了簡單的介紹,並且對意圖,結構,角色,使用形式也進行了介紹,另外還給出了Java的狀態模式的實現。 ...
狀態模式 State 人有喜怒哀樂,海綿寶寶也會有不同的時候,也會有不同的心情~ 問題:上圖中,如果跟海綿寶寶開玩笑,那種情況最可能被打? 看下麵一個示例,演示了java中的多態特性 類A有方法action() 類B繼承了A 覆蓋了方法action() 類C繼承了A 覆蓋了方法action()
package state.example; public class A { void action() { System.out.println("A......."); } public static void main(String[] args) { A a = new A(); A b = new B(); A c = new C(); a.action(); b.action(); c.action(); } } class B extends A { @Override void action() { System.out.println("B......."); } class C extends A { @Override void action() { System.out.println("C......."); } }類型都是A,但是卻因為內部的具體的子類類型不一樣,結果不一樣 海綿寶寶在不同心情狀態下,對同一件事情的處理態度可能是不同的,生氣的時候跟他開玩笑很可能會被打。 同樣的類型A,由於具體類型狀態的不同,給出的響應是不同的。 這就是狀態,很多事物都有不同的狀態,不同的狀態可能會有不同的行為,而且,狀態間是可以切換的。
意圖
允許一個對象在其內部狀態改變時改變他的行為。對象看起來似乎修改了他的類。 別名:狀態對象(Objects for States) 其實就是擁有狀態屬性,在不同的狀態下,呈現出不同的行為,並且可以靈活的切換狀態。 狀態:一個對象的行為取決於內部一個或者多個動態變化的屬性,這樣的屬性就叫做狀態。 有狀態的對象stateful:這樣的對象就叫做有狀態的對象。結構
不考慮Java語言的多態,如何達到“多態”的效果? 一種很自然的解決方案就是條件分支或者選擇語句,比如int state = 0; if(0 == state){ //... }else if(1 == state){ //... }else if(2 == state){ //... }else{ //... }int類型的變數state 就是“有狀態的對象”,state的具體的值就是“狀態”。 狀態的切換依賴state變數的賦值,不同行為的呈現藉助於條件分支。 顯然,如果業務邏輯複雜,將會有繁瑣複雜的分支判斷 更有甚者,如果有多個狀態共同決定行為,那麼豈不是多個狀態的複雜組合了? 而且如果新增加狀態,那麼就需要修改代碼,添加一個新的 else if(),擴展性不好,不符合開閉原則。 我們更希望的是能夠達到“多態”的那種調用效果,方法調用與具體的行為解耦。 調用者不需要關註具體的類型,在方法執行時,會具有真實類型的行為。 相當於變形為:
int state = 0; state.action();看起來,看起來好像,看起來好像action()方法封裝了類似下麵的判斷邏輯(只是看起來,其實他只有他自己狀態下的行為,不需要判斷)
if(0 == state){ //... }else if(1 == state){ //... }else if(2 == state){ //... }else{ //... }這不就是上面的多態示例麽,如果A表示抽象的狀態,B和C表示具體的某種狀態 如果所有使用狀態的地方,都使用靜態類型A,就可以根據實際類型達到多態的效果了。 所以說 狀態模式的根本在於藉助於OOP的多態機制,描述狀態,就可以達到不同行為的效果。 有了不同類型的狀態之後,還可以進一步通過一個中間類對他們進行管理,進而實現靈活的狀態切換。 所以一種常用的狀態模式結果如下 抽象狀態角色State 介面或者抽象類,定義狀態的抽象含義,並且給出狀態的行為介面,這個介面被外界通過環境類調用 可以持有Context的引用,通過Context完成狀態切換 具體狀態ConcreteState 實現了抽象狀態的描述,給出自己的行為,每一個子類型都表示一種具體的狀態 環境類角色Context 又叫做上下文,通過環境類Context對狀態進行管理 一般做法是將多種狀態定義為他的靜態屬性,環境類中維護一個State,這是當前狀態,經常提供切換狀態的方法
代碼示例
抽象的State,定義狀態行為package state; public interface State { void handle(); }具體的狀態1
package state; public class ConcreateState1 implements State { @Override public void handle() { System.out.println("state 1 do sth"); } }具體的狀態2
package state; public class ConcreateState2 implements State { @Override public void handle() { System.out.println("state 2 do sth"); } }環境類,內部封裝了兩個狀態 state為當前狀態,初始時設置為狀態1了 通過changeState方法進行切換 action()方法為封裝調用state的handle方法
package state; public class Context { private final State STATE1 = new ConcreateState1(); private final State STATE2 = new ConcreateState2(); private State state = STATE1; public void action() { state.handle(); } public void changeState(int stateValue) { if (1 == stateValue) { state = STATE1; } else if (2 == stateValue) { state = STATE2; } } }上面的示例代碼中,初始狀態為狀態1 ,action調用狀態1,列印 state 1 do sth 狀態切換後,調用狀態2 狀態模式的核心在於引入抽象狀態角色State,藉助於多態,實現不同的行為, 然後藉助於環境類Context實現State的管理,以靈活切換狀態。
狀態的具體行為,狀態切換的時機等等都可以根據實際情況靈活處理,尤其是何時切換狀態,誰來負責切換狀態。 示例中,在環境類Context中創建了靜態的狀態對象,你可以根據實際情況動態的創建對象。 如果狀態頻繁變化,那麼事先創建所有狀態更合適,如果狀態設置後就很少變動,動態創建的形式或許更好。 狀態也可以持有環境類的引用,可以獲得更多的便利性。
另外的示例
如果僅僅是狀態的提取,簡化複雜的狀態判斷邏輯,藉助於State角色就可以完成 如果需要狀態的切換維護,或者要求狀態的順序等,總之對State的訪問需要增加更多的業務邏輯時,那麼就可以藉助於Context對State進行管理 通過Context封裝維護當前狀態,也就是Context提供代理方法,代理對當前狀態State的直接訪問 這樣就可以通過Context增加狀態切換,順序限制等更多的處理邏輯 比如下麵邏輯,假設狀態1,2,3,4,5,通過下麵的邏輯,就可以控制狀態的切換順序,當前狀態推導出下一個狀態void click(){
currentState.handle();
if(state == 1){
changeState(2);
}else if(state == 2){
changeState(3);
}
//.....
}
package state.order; public interface State { void handle(); }
package state.order; public class ConcreateState1 implements State { @Override public void handle() { System.out.println("state 1 do sth"); } }
package state.order; public class ConcreateState2 implements State { @Override public void handle() { System.out.println("state 2 do sth"); } }
package state.order; public class ConcreateState3 implements State { @Override public void handle() { System.out.println("state 3 do sth"); } }環境Context包括三種狀態,初始時當前狀態為狀態1 每次方法調用,都會按照順序切換狀態
package state.order; public class Context { private final State STATE1 = new ConcreateState1(); private final State STATE2 = new ConcreateState2(); private final State STATE3 = new ConcreateState3(); private State state = STATE1; public void action() { state.handle(); if (state == STATE1) { setState(STATE2); } else if (state == STATE2) { setState(STATE3); } else if (state == STATE3) { setState(STATE1); } } void setState(State state) { this.state = state; } }從上面的結果可以看得出來,不管你調用多少次方法,他都會完成兩個任務,當前狀態處理,然後切換狀態 所以說,狀態的切換時機,場景,不同的方式將會有很多種不同的變化形式。