本文從簡單的例子入手,逐步演變成非常復雜的程序。
在簡明 狀態模式(5.8)中,狀態之間的變換由外界控制,或者說,多種狀態是分割的、無關的。狀態模式最有趣的地方正是討論其狀態的變遷。
1.引子
空調(air-condition)的遙控器有兩個按鈕(更多的按鈕在后面的例子中引入),power/電源鍵和cool/制冷鍵。空調的運行呈現3個狀態,停止/Off、僅送風/FanOnly、制冷/Cool。起始狀態為Off,狀態變化圖如下所示。
這是簡化的有限狀態機(Finite State Machine、FSM或者Finite State Automata)圖形,使用了狀態圖的3個元素:①氣泡,表示狀態(state);②連接狀態的箭頭表示轉換(transition);③箭頭上的標記前者為事件(event)。
狀態的轉換,看圖說話。按power鍵,則Off→FanOnly、Cool→Off等;按cool,則Off→Off (沒有畫出來,喜歡全面一點就自己畫吧)。
對於這種簡單的狀態的轉換,yqj2065還是喜歡分支語句。,簡潔明快。
[java] view plain copy 例程 4-5 簡潔明快 package property.state.stateMachine; import static tool.Print.*;//pln /** * 空調Aircon。簡單的模型: * 遙控器有兩個按鈕(更多的按鈕在下面的例子中引入),power電源鍵和cool制冷鍵。 * 空調的運行呈現3個狀態,停止/Off、僅送風/FanOnly、制冷/Cool。 * 起始狀態為Off * @author (yqj2065) * @version 0.1 */ public class Aircon0{ // off,FanOnly,AC private int state=0;//起始狀態為Off public int getState(){return state;} //兩個Action public void power(){//按power鍵 if(state==0){//off state=1; pln("start Fan"); }else if(state==1){//FanOnly state=0; pln("stop Fan"); }else{ state=0; pln("stop Cool"); } } public void cool(){//按制冷鍵 if(state==0){//off pln("nothing"); }else if(state==1){//FanOnly state=2; pln("start Cool"); }else{ state=1; pln("stop Cool"); } } } package property.state.stateMachine; public class ACCtrl{ public static void test(){ Aircon0 ac = new Aircon0();//power() cool() System.out.println("Current State:" + ac.getState()); ac.cool(); ac.power(); ac.cool(); ac.cool(); ac.power(); ac.power(); } } 測試代碼的輸出: Current State:0 nothing start Fan start Cool stop Cool stop Fan start Fan
在此基礎上,可以花10分鍾練習一下,采用狀態模式修改上述代碼。我們使用enum編寫狀態類層次。其結構如下:
[java] view plain copy 例程 4 6 enum State enum State0{ OFF{ @Override void power(){ } @Override void power(){ } },FANONLY{ }, COOL{ }; public abstract void power(); public abstract void cool(); } (本來是應該將State1作為Aircon1的內部類的。放在外邊,power()等需要添加參數Aircon1,變為power(Aircon1 ac)). 現在,豐富有限狀態機的細節,增添④動作(action),如事件(event)相應的動作和狀態的動作。 為此,在enum State1中,除了狀態模式 提取的接口外,添加了狀態機的各種動作/action methode void entry(Aircon1 ac){pln("→"+ac.state.name());} void exit(Aircon1 ac){p(ac.state.name()+"→ ");} void startCool(){ p("start Cool"); } void stopCool(){ p("stop Cool"); } void startFan(){ p("start Fan"); } void stopFan(){ p("stop Fan"); } 每個power(Aircon1 ac)、cool(Aircon1 ac)的方法體結構都是: this.exit(ac); //如果有的話,事件(event)相應的動作,如stopFan(); ac.state =OFF; //下一個狀態 ac.state.entry(ac); [java] view plain copy package property.state.stateMachine; import static tool.Print.*;//pln /** * 本來是應該將State1作為Aircon1的內部類的。現在放在外邊, * power()等需要變為power(Aircon1 ac) */ public enum State1{ OFF{ @Override void exit(Aircon1 ac){super.exit(ac);startFan();} @Override void power(Aircon1 ac){ this.exit(ac); ac.state =FANONLY; ac.state.entry(ac); } @Override void cool(Aircon1 ac){ pln("nothing"); } },FANONLY{ @Override void power(Aircon1 ac){ this.exit(ac); stopFan(); ac.state =OFF; ac.state.entry(ac); } @Override void cool(Aircon1 ac){ this.exit(ac); ac.state =COOL; ac.state.entry(ac); } }, COOL{ @Override void exit(Aircon1 ac){super.exit(ac);stopCool();} @Override void entry(Aircon1 ac){startCool();super.entry(ac);} @Override void power(Aircon1 ac){ this.exit(ac); stopFan(); ac.state =OFF; ac.state.entry(ac); } @Override void cool(Aircon1 ac){ this.exit(ac); ac.state =FANONLY; ac.state.entry(ac); } }; //狀態模式 提取的接口 abstract void power(Aircon1 ac); abstract void cool(Aircon1 ac); //狀態機的各種動作action methode void entry(Aircon1 ac){pln("→"+ac.state.name());} void exit(Aircon1 ac){p(ac.state.name()+"→ ");} void startCool(){ p("start Cool"); } void stopCool(){ p("stop Cool"); } void startFan(){ p("start Fan"); } void stopFan(){ p("stop Fan"); } }
空調Aircon1的修改版本。
[java] view plain copy package property.state.stateMachine; import static tool.Print.*;//pln /** * 空調Aircon1。使用狀態模式重構Aircon0,使用enum State1編寫狀態類層次。 * @author (yqj2065) * @version 0.1 */ public class Aircon1{ State1 state= State1.OFF;//private改默認,刪除getState()。 //兩個Action public void power(){//按power鍵 state.power(this); } public void cool(){//按制冷鍵 state.cool(this); } /** * ACCtrl的代碼。 */ public static void test(){ Aircon1 ac = new Aircon1(); System.out.println("Current State:" + ac.state.name()); ac.cool(); ac.power(); ac.cool(); ac.cool(); ac.power(); ac.power(); ac.power(); } }
對應測試操作的輸出:“OFF→”表示離開OFF狀態,而“→FANONLY”...
Current State:OFF
nothing
OFF→ start Fan→FANONLY
FANONLY→ start Cool→COOL
COOL→ stop Cool→FANONLY
FANONLY→ stop Fan→OFF
OFF→ start Fan→FANONLY
FANONLY→ stop Fan→OFF
2.分層狀態機
對於狀態較多的狀態機,通常將具有許多公共的特性的狀態合並到一起。例如FANONLY和COOL構成的Running狀態。
狀態機中的hierarchical states,我們可以使用組合模式處理。(還沒有單獨寫組合模式,)。但是,又不一定能夠完美地使用組合模式,例如Running到Off,所有的Running的內部狀態在PoverEvent時都轉化到OFF,很好;OFF到Running,不是所有Running的內部狀態都要調用其entry。在使用enum(不好搞類層次)時,使用責任鏈吧。
搜集摘抄於:http://blog.csdn.net/yqj2065/article/details/39371487