What
狀態(tài)模式(State Pattern),允許一個對象在其內(nèi)部狀態(tài)改變的時候改變其行為。這個對象看上去就像是改變了它的類一樣。狀態(tài)模式是一種對象行為型模式。
Why
- 狀態(tài)模式可以通過將事件觸發(fā)的狀態(tài)轉(zhuǎn)移和動作執(zhí)行,拆分到不同的狀態(tài)類中,來避免分支判斷邏輯。
- 狀態(tài)模式可以封裝轉(zhuǎn)換規(guī)則。
- 枚舉可能的狀態(tài),在枚舉狀態(tài)之前需要確定狀態(tài)種類。
- 將所有與某個狀態(tài)有關(guān)的行為放到一個類中,并且可以方便地增加新的狀態(tài),只需要改變對象狀態(tài)即可改變對象的行為。
- 允許狀態(tài)轉(zhuǎn)換邏輯與狀態(tài)對象合成一體,而不是某一個巨大的條件語句塊。
- 可以讓多個環(huán)境對象共享一個狀態(tài)對象,從而減少系統(tǒng)中對象的個數(shù)。
對于這些優(yōu)點,大家可以從下面具體的實例中去感受。
When
在以下情況下可以使用狀態(tài)模式:
- 對象的行為依賴于它的狀態(tài)(屬性)并且可以根據(jù)它的狀態(tài)改變而改變它的相關(guān)行為。
- 代碼中包含大量與對象狀態(tài)有關(guān)的條件語句,這些條件語句的出現(xiàn),會導(dǎo)致代碼的可維護性和靈活性變差,不能方便地增加和刪除狀態(tài),使客戶類與類庫之間的耦合增強。在這些條件語句中包含了對象的行為,而且這些條件對應(yīng)于對象的各種狀態(tài)。
How
用一句話來表述,狀態(tài)模式把所研究的對象的行為包裝在不同的狀態(tài)對象里,每一個狀態(tài)對象都屬于一個抽象狀態(tài)類的一個子類。狀態(tài)模式的意圖是讓一個對象在其內(nèi)部狀態(tài)改變的時候,其行為也隨之改變。
《超級馬里奧》這款游戲,大家應(yīng)該都玩過吧。馬里奧的狀態(tài)變換規(guī)則如下:
- 游戲一開始,小馬里奧從天而降,歡樂的蹦噠著,雖然金錢袋空空如也;
- 當(dāng)小馬里奧吃掉蘑菇后,小馬奧就變身大馬里奧,并且可以得到一定金錢作為獎勵;
- 當(dāng)小馬里奧碰到怪獸時,小馬里奧就直接gg了,金錢袋直接變空了;
- 當(dāng)大馬里奧碰到怪獸時,大馬里奧就一波回到解放前,變成了小馬里奧,并扣除一部分金錢。
以上馬里奧的狀態(tài)變化伴隨金錢的增加與減少,這個案例特別適合使用狀態(tài)模式來實現(xiàn)。 下面,我們就來用代碼實現(xiàn)下該需求。
首先,我們定義一個接口,實現(xiàn)該接口可以實現(xiàn)不同狀態(tài)的馬里奧。該接口下,定義事件,即吃蘑菇和碰到怪獸。
public interface IMario {
State getState();
void obtainMushRoom();
void meetMonster();
}
public class SmallMario implements IMario {
private MarioStateMachine marioStateMachine;
public SmallMario(MarioStateMachine marioStateMachine) {
this.marioStateMachine = marioStateMachine;
}
@Override
public State getState() {
return State.SMALL;
}
@Override
public void obtainMushRoom() {
System.out.println("Small Mario obtain mushroom");
marioStateMachine.addMoney(100);
marioStateMachine.setState(new SuperMario(marioStateMachine));
System.out.println("Add money: " + 100);
System.out.println("Current state: " + marioStateMachine.getState().getState());
}
@Override
public void meetMonster() {
System.out.println("Small Mario meets monster");
marioStateMachine.addMoney(200);
System.out.println("Sub money: " + 200);
System.out.println("Current state: " + marioStateMachine.getState().getState());
}
}
public class SuperMario implements IMario {
private MarioStateMachine marioStateMachine;
public SuperMario(MarioStateMachine marioStateMachine) {
this.marioStateMachine = marioStateMachine;
}
@Override
public State getState() {
return State.SUPER;
}
@Override
public void obtainMushRoom() {
System.out.println("Super Mario obtain mush room");
this.marioStateMachine.addMoney(100);
System.out.println("add money: " + 100);
}
@Override
public void meetMonster() {
System.out.println("Super Mario meets monster");
this.marioStateMachine.setState(new SmallMario(marioStateMachine));
this.marioStateMachine.subMoney(200);
System.out.println("sub money: " + 200);
System.out.println("current state: " + marioStateMachine.getState().getState());
}
}
接下來,我們定義下狀態(tài)機類,該類下包含兩個成員屬性——金錢(money)和狀態(tài)(state),注意狀態(tài)(state)變量需要在狀態(tài)變化時傳入下一個狀態(tài),就像上面SmallMario中的obtainMushRoom方法,需要調(diào)用this.marioStateMachine.setState(new SuperMario(this.marioStateMachine)),這樣marioStateMachine.getState()時就會返回更新后的狀態(tài)。
public class MarioStateMachine {
private Integer money;
private IMario state;
public Integer getMoney() {
return money;
}
public IMario getState() {
return state;
}
public void setState(IMario state) {
this.state = state;
}
public MarioStateMachine() {
this.money = 0;
this.state = new SmallMario(this);
}
public void addMoney(Integer prize) {
this.money += prize;
}
public void subMoney(Integer punishment) {
this.money -= punishment;
}
}
這里可能比較繞,可以好好思考下。
最后,來個測試類:
public class TestMain {
public static void main(String[] args) {
MarioStateMachine marioStateMachine = new MarioStateMachine();
marioStateMachine.getState().obtainMushRoom();
marioStateMachine.getState().meetMonster();
marioStateMachine.getState().meetMonster();
Integer money = marioStateMachine.getMoney();
System.out.println("Final money: " + money);
}
}
輸出如下:
Small Mario obtain mushroom
Add money: 100
Current state: SUPER
Super Mario meets monster
sub money: 200
current state: SMALL
Small Mario meets monster
Sub money: 200
Current state: SMALL
Final money: 100
代碼地址
寫在最后
如果你覺得我寫的文章幫到了你,歡迎點贊、評論、分享、贊賞哦,你們的鼓勵是我不斷創(chuàng)作的動力~