一個(gè)物體可能存在多種類型的狀態(tài)妥曲,納悶狀態(tài)之間就會(huì)存在轉(zhuǎn)換的關(guān)系垦写。例如液態(tài)的水,可以氣化成為水蒸氣,也可以固話成為冰塊戴陡,而冰塊可以通過升華直接成為水蒸氣塞绿,水蒸氣反過來通過凝華也能夠化為冰。
下面我畫出了轉(zhuǎn)換過程的狀態(tài)圖恤批,其中省略了升華和凝華的步驟(太多了好麻煩耙煳恰)
在這個(gè)圖中我們能看到一共有3種狀態(tài):
- 固體(Solid)
- 液體(Liquid)
- 氣體(Gas)
另外還存在4種轉(zhuǎn)換方式:
- 凝固(Freeze)
- 融化(Melt)
- 液化(Liquidation)
- 氣化(Gasification)
那如果我們想表現(xiàn)Water這一種物質(zhì)在不同狀態(tài)下的轉(zhuǎn)換流程和關(guān)系的話,如果使用丑陋的if語句喜庞,則會(huì)時(shí)代碼陷入萬劫不復(fù)的境地:
if( state == Solid){
....
}else if(state == Liquid){
...
}
而狀態(tài)模式就是來解決這一類狀態(tài)轉(zhuǎn)換的問題的模式诀浪,它的要求就是將特定狀態(tài)時(shí)的行為和實(shí)現(xiàn),通過狀態(tài)類的方式進(jìn)行定義和封裝延都。當(dāng)一個(gè)類內(nèi)部的狀態(tài)改變時(shí)雷猪,類的行為的具體實(shí)現(xiàn)方式也會(huì)隨之而改變。
下面我們來看一下狀態(tài)模式的UML圖是什么樣子的:
看UML圖其實(shí)很簡單晰房,就是傳統(tǒng)的類實(shí)現(xiàn)接口求摇,而客戶直接調(diào)用接口。但其實(shí)狀態(tài)模式的關(guān)鍵點(diǎn)在于:
- 狀態(tài)的轉(zhuǎn)換需要反饋回調(diào)用方殊者。
- 狀態(tài)不需要完全實(shí)現(xiàn)父類定義的所有方法与境。
- 狀態(tài)類與宿主類之間的耦合關(guān)系。
帶著上面所寫的三個(gè)關(guān)鍵點(diǎn)猖吴,我們來嘗試實(shí)現(xiàn)上面所說的水的狀態(tài)變化過程摔刁。
0. 定義State抽象類
上面說了其實(shí)一共有4種轉(zhuǎn)換方式,那我們就需要在抽象類中定義4個(gè)方法距误,但是由于不是每一種狀態(tài)都會(huì)發(fā)生著4種轉(zhuǎn)換的簸搞,比如說已經(jīng)是水蒸氣了,就無法再進(jìn)行氣化了准潭。所以我們在抽象類中趁俊,仍然實(shí)現(xiàn)了所有的防范,但是方法體都是空的刑然。這與我們在Java設(shè)計(jì)模式之-策略模式中說到的Null Object其實(shí)有著異曲同工之妙寺擂。:
public abstract class WaterState{
public WaterState freeze(){return null;}
public WaterState melt(){return null;}
public WaterState liquidation(){return null;}
public WaterState gasification(){return null;}
}
然后我們來實(shí)現(xiàn)這三種狀態(tài):
public class Solid extends WaterState{
public WaterState melt(Water water){
return water.LIQUID;
}
}
public class Liquid extends WaterState{
public WaterState freeze(Water water){
return water.SOLID;
}
public WaterState gasification(Water water){
return water.GAS;
}
}
public class Gas extends WaterState{
public WaterState liquidation(Water water){
return water.LIQUID;
}
目前我們的狀態(tài)機(jī)已經(jīng)配置完畢了,現(xiàn)在來看一下對應(yīng)的水應(yīng)該如何寫:
public class Water{
public static final WaterState LIQUID = new Liquid();
public static final WaterState SOLID = new Solid();
public static final WaterState GAS = new Gas();
private WaterState state = new Liquid(); // liquid is default state.
public getState(){return state;}
public setState(WaterState state){this.state = state;}
public void freeze(){
WaterState tmp = state.freeze(this);
if(tmp != null)state = tmp;
}
public WaterState melt(){
WaterState tmp = state.melt(this);
if(tmp != null)state = tmp;
}
public WaterState liquidation(){
WaterState tmp = state.liquidation(this);
if(tmp != null)state = tmp;
}
public WaterState gasification(){
WaterState tmp = state.gasification(this);
if(tmp != null)state = tmp;
}
}
以上我們便實(shí)現(xiàn)了一個(gè)狀態(tài)模式的代碼泼掠,其中關(guān)于上面3點(diǎn)怔软,有如下回復(fù):
- 在狀態(tài)機(jī)的轉(zhuǎn)換方法中,返回了需要轉(zhuǎn)換的目標(biāo)態(tài)择镇,直接交給調(diào)用方進(jìn)行使用挡逼;
- 狀態(tài)在實(shí)現(xiàn)過程中,只復(fù)寫了1-2個(gè)方法腻豌,并沒有將所有方法都復(fù)寫家坎,因?yàn)橛行┎贿m用于當(dāng)前狀態(tài)嘱能;
- 狀態(tài)類于宿主之間通過參數(shù)傳遞的方式進(jìn)行交互,只存在松耦合關(guān)系虱疏;
另外以上例子也可以用Enum來實(shí)現(xiàn)惹骂,這里就不再演示了。