有限狀態機(FSM)的Java 演示


本文從簡單的樣例入手。逐步演變成很復雜的程序。

簡明 狀態模式(5.8)中,狀態之間的變換由外界控制,或者說。多種狀態是切割的、無關的。狀態模式最有趣的地方正是討論其狀態的變遷。

1.引子

空調(air-condition)的遙控器有兩個button(很多其它的button奮斗在后面的樣例中引入)。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還是喜歡分支語句微笑,簡潔明快。

例程 4-5 簡潔明快
package property.state.stateMachine;
import static tool.Print.*;//pln
/**
 * 空調Aircon。

簡單的模型: * 遙控器有兩個button(很多其它的button在以下的樣例中引入)。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編寫狀態類層次

其結構例如以下:

例程 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); 

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的改動版本號。


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(不好搞類層次)時。使用責任鏈吧。


樓主繪圖中、考慮很多其它button中....



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM