本文從簡單的例子入手,逐步演變成非常復雜的程序。
在簡明 狀態模式(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
