有限狀態機(FSM)的Java 學習FSM


 本文從簡單的例子入手,逐步演變成非常復雜的程序。

在簡明 狀態模式(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


免責聲明!

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



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