設(shè)計模式系列篇(十七)——狀態(tài)模式

What

狀態(tài)模式(State Pattern),允許一個對象在其內(nèi)部狀態(tài)改變的時候改變其行為。這個對象看上去就像是改變了它的類一樣。狀態(tài)模式是一種對象行為型模式。

Why

  1. 狀態(tài)模式可以通過將事件觸發(fā)的狀態(tài)轉(zhuǎn)移和動作執(zhí)行,拆分到不同的狀態(tài)類中,來避免分支判斷邏輯。
  2. 狀態(tài)模式可以封裝轉(zhuǎn)換規(guī)則。
  3. 枚舉可能的狀態(tài),在枚舉狀態(tài)之前需要確定狀態(tài)種類。
  4. 將所有與某個狀態(tài)有關(guān)的行為放到一個類中,并且可以方便地增加新的狀態(tài),只需要改變對象狀態(tài)即可改變對象的行為。
  5. 允許狀態(tài)轉(zhuǎn)換邏輯與狀態(tài)對象合成一體,而不是某一個巨大的條件語句塊。
  6. 可以讓多個環(huán)境對象共享一個狀態(tài)對象,從而減少系統(tǒng)中對象的個數(shù)。

對于這些優(yōu)點,大家可以從下面具體的實例中去感受。

When

在以下情況下可以使用狀態(tài)模式:

  1. 對象的行為依賴于它的狀態(tài)(屬性)并且可以根據(jù)它的狀態(tài)改變而改變它的相關(guān)行為。
  2. 代碼中包含大量與對象狀態(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ī)則如下:

  1. 游戲一開始,小馬里奧從天而降,歡樂的蹦噠著,雖然金錢袋空空如也;
  2. 當(dāng)小馬里奧吃掉蘑菇后,小馬奧就變身大馬里奧,并且可以得到一定金錢作為獎勵;
  3. 當(dāng)小馬里奧碰到怪獸時,小馬里奧就直接gg了,金錢袋直接變空了;
  4. 當(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

代碼地址

i-learning

寫在最后

如果你覺得我寫的文章幫到了你,歡迎點贊、評論、分享、贊賞哦,你們的鼓勵是我不斷創(chuàng)作的動力~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容