狀態模式--黑崎一護的嗑藥歷程

變身藥丸

黑崎一護在和藍染戰鬥中,使用了最後的月牙天衝重創藍染,最終被被浦原喜助使用封印封鎖。

一護立下大功,同時也喪失了死神的力量,無法使用斬魂刀變身了。

十二番隊長涅繭利爲了感謝一戶,苦心研發了能夠暫時獲得死神能力的三種藥丸:初解藥丸,卍解藥丸和虛化藥丸。

這三種藥丸服用之後能夠變身成相應的狀態,同時擁有對應的必殺技,戰鬥力大大增強。

黑崎一護的變身狀態一覽

正常狀態

戰鬥力指數:100
在這裏插入圖片描述

始解狀態

戰鬥力指數:200
在這裏插入圖片描述

卍解狀態

戰鬥力指數:500
在這裏插入圖片描述

虛化狀態

戰鬥力指數:1000
在這裏插入圖片描述
十二番隊長涅繭利特意囑咐了下用藥規則,正常狀態下只有喫始解藥丸和卍解藥丸纔有作用,虛化藥丸只有在卍解狀態下喫纔有作用,還有,同一種藥丸重複喫是沒有用的,不要浪費哦。

​另外,變身狀態下,使用了必殺技之後,藥效就會消失,立即恢復到正常狀態,要注意哦,副隊長涅音夢提醒道。
在這裏插入圖片描述
貼心的副隊長涅音夢還畫了幅狀態轉換圖:
在這裏插入圖片描述

普通的實現方法

狀態枚舉類

public enum State {

    /**
     * 正常狀態
     */
    NORMAL(0),
    /**
     * 始解狀態
     */
    INITIAL(1),
    /**
     * 卍解狀態
     */
    SWASTIKA(2),
    /**
     * 虛化狀態
     */
    BLUR(3);

    private int value;

    private State(int value) {
        this.value = value;
    }

    public int getValue() {
        return this.value;
    }
}

變身狀態機類

public class TransformStateMachine {

    /**
     * 戰鬥力
     */
    private int fightingPower;
    /**
     * 當前狀態
     */
    private State currentState;


    public int getFightingPower() {
        return this.fightingPower;
    }

    public State getCurrentState() {
        return this.currentState;
    }

    /**
     * 初始化
     */
    public TransformStateMachine() {
        this.fightingPower = 100;
        this.currentState = State.NORMAL;
    }

    /**
     * 喫下始解藥丸
     */
    public void eateInitialPill() {
        System.out.println("喫下始解藥丸");
        if(currentState.equals(State.NORMAL)) {
            this.currentState = State.INITIAL;
            this.fightingPower += 200;
            System.out.println("**變身始解狀態**戰鬥力加200");
        } else {
            System.out.println("木有任何變化。。。");
        }
    }

    /**
     * 喫下卍解藥丸
     */
    public void eateSwastikaPill() {
        System.out.println("喫下卍解藥丸");
        if(currentState.equals(State.NORMAL) || currentState.equals(State.INITIAL)) {
            this.currentState = State.SWASTIKA;
            this.fightingPower += 500;
            System.out.println("**變身卍解狀態**戰鬥力加500");
        } else {
            System.out.println("木有任何變化。。。");
        }
    }

    /**
     * 喫下虛化藥丸
     */
    public void eateBlurPill() {
        System.out.println("喫下虛化藥丸");
        if(currentState.equals(State.SWASTIKA)) {
            this.currentState = State.BLUR;
            this.fightingPower += 1000;
            System.out.println("**變身虛化狀態**戰鬥力加1000");
        } else {
            System.out.println("木有任何變化。。。");
        }
    }

    /**
     * 使用必殺技
     */
    public void useUniqueSkill() {
        System.out.println("使用必殺技");
        if(currentState.equals(State.INITIAL) || currentState.equals(State.SWASTIKA) || currentState.equals(State.BLUR)) {
            System.out.println("月牙天衝--->");
            this.currentState = State.NORMAL;
            this.fightingPower = 100;
            System.out.println("**變回初始狀態**戰鬥力變成100");
        } else {
            System.out.println("木有任何變化。。。");
        }
    }
}

測試類

public class Test {

    public static void main(String[] args) {
        TransformStateMachine transformStateMachine = new TransformStateMachine();
        System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
        transformStateMachine.eateInitialPill();
        System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
        transformStateMachine.eateSwastikaPill();
        System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
        transformStateMachine.eateBlurPill();
        System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
        transformStateMachine.useUniqueSkill();
        System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
        transformStateMachine.useUniqueSkill();
        System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
    }
}

運行結果

當前狀態: NORMAL; 戰鬥力指數: 100

喫下始解藥丸
**變身始解狀態**戰鬥力加200
當前狀態: INITIAL; 戰鬥力指數: 300

喫下卍解藥丸
**變身卍解狀態**戰鬥力加500
當前狀態: SWASTIKA; 戰鬥力指數: 800

喫下虛化藥丸
**變身虛化狀態**戰鬥力加1000
當前狀態: BLUR; 戰鬥力指數: 1800

使用必殺技
月牙天衝--->
**變回初始狀態**戰鬥力變成100
當前狀態: NORMAL; 戰鬥力指數: 100

使用必殺技
木有任何變化。。。
當前狀態: NORMAL; 戰鬥力指數: 100

如果是簡單的狀態轉換來說,這種方式是最簡單直接的首選方法。

如果狀態很多,這樣編寫的代碼會包含大量的 if-else 或 switch-case 分支判斷邏輯,可讀性和可維護性都很差。

而且不符合開閉原則,如果修改了狀態機中的某個狀態轉移,我們要在冗長的分支邏輯中找到對應的代碼進行修改,很容易改錯,引入 bug。

使用狀態模式

我們要重構一下上面的代碼,試着把每個狀態的行爲封裝到一個類裏,“封裝了變化”,這樣能讓邏輯和結構更加清晰。

而且,我們針對某個狀態的改變就不會影響其他的狀態了,讓修改和維護更容易。

這時候就要請狀態模式出馬了。

狀態模式(State Pattern) :允許一個對象在其內部狀態改變時改變它的行爲,對象看起來似乎修改了它的類。

在這裏插入圖片描述
本例的類圖如下:
在這裏插入圖片描述
狀態枚舉類

public enum State {

    /**
     * 正常狀態
     */
    NORMAL(0),
    /**
     * 始解狀態
     */
    INITIAL(1),
    /**
     * 卍解狀態
     */
    SWASTIKA(2),
    /**
     * 虛化狀態
     */
    BLUR(3);

    private int value;

    private State(int value) {
        this.value = value;
    }

    public int getValue() {
        return this.value;
    }
}

狀態類接口

public interface ITransformState {

    /**
     * 獲取當前狀態
     * @return
     */
    State getState();

    /**
     * 喫下始解藥丸
     */
    void eateInitialPill();

    /**
     * 喫下卍解藥丸
     */
    void eateSwastikaPill();

    /**
     * 喫下虛化藥丸
     */
    void eateBlurPill();

    /**
     * 使用必殺技
     */
    void useUniqueSkill();
}

初始狀態實現類

public class NormalTransform implements ITransformState{

    private TransformStateMachine transformStateMachine;

    public NormalTransform(TransformStateMachine transformStateMachine) {
        this.transformStateMachine = transformStateMachine;
    }

    /**
     * 獲取當前狀態
     * @return
     */
    public State getState() {
        return State.NORMAL;
    }

    /**
     * 喫下始解藥丸
     */
    public void eateInitialPill() {
        System.out.println("喫下始解藥丸");
        transformStateMachine.setCurrentState(new InitialTransform(transformStateMachine));
        transformStateMachine.setFightingPower(transformStateMachine.getFightingPower() + 200);
        System.out.println("**變身始解狀態**戰鬥力加200");
    }

    /**
     * 喫下卍解藥丸
     */
    public void eateSwastikaPill() {
        System.out.println("喫下卍解藥丸");
        transformStateMachine.setCurrentState(new SwastikaTransform(transformStateMachine));
        transformStateMachine.setFightingPower(transformStateMachine.getFightingPower() + 500);
        System.out.println("**變身卍解狀態**戰鬥力加500");
    }

    /**
     * 喫下虛化藥丸
     */
    public void eateBlurPill() {
        System.out.println("喫下虛化藥丸");
        System.out.println("木有任何變化。。。");
    }

    /**
     * 使用必殺技
     */
    public void useUniqueSkill() {
        System.out.println("使用必殺技");
        System.out.println("木有任何變化。。。");
    }
}

始解狀態實現類

public class InitialTransform implements ITransformState{

    private TransformStateMachine transformStateMachine;

    public InitialTransform(TransformStateMachine transformStateMachine) {
        this.transformStateMachine = transformStateMachine;
    }

    /**
     * 獲取當前狀態
     * @return
     */
    public State getState() {
        return State.INITIAL;
    }

    /**
     * 喫下始解藥丸
     */
    public void eateInitialPill() {
        System.out.println("喫下始解藥丸");
        System.out.println("木有任何變化。。。");
    }

    /**
     * 喫下卍解藥丸
     */
    public void eateSwastikaPill() {
        System.out.println("喫下卍解藥丸");
        transformStateMachine.setCurrentState(new SwastikaTransform(transformStateMachine));
        transformStateMachine.setFightingPower(transformStateMachine.getFightingPower() + 500);
        System.out.println("**變身卍解狀態**戰鬥力加500");
    }

    /**
     * 喫下虛化藥丸
     */
    public void eateBlurPill() {
        System.out.println("喫下虛化藥丸");
        System.out.println("木有任何變化。。。");
    }

    /**
     * 使用必殺技
     */
    public void useUniqueSkill() {
        System.out.println("使用必殺技");
        System.out.println("月牙天衝--->");
        transformStateMachine.setCurrentState(new NormalTransform(transformStateMachine));
        transformStateMachine.setFightingPower(100);
        System.out.println("**變回初始狀態**戰鬥力變成100");
    }
}

卍解狀態實現類

public class SwastikaTransform implements ITransformState {

    private TransformStateMachine transformStateMachine;

    public SwastikaTransform(TransformStateMachine transformStateMachine) {
        this.transformStateMachine = transformStateMachine;
    }

    /**
     * 獲取當前狀態
     * @return
     */
    public State getState() {
        return State.SWASTIKA;
    }

    /**
     * 喫下始解藥丸
     */
    public void eateInitialPill() {
        System.out.println("喫下始解藥丸");
        System.out.println("木有任何變化。。。");
    }

    /**
     * 喫下卍解藥丸
     */
    public void eateSwastikaPill() {
        System.out.println("喫下始解藥丸");
        System.out.println("木有任何變化。。。");
    }

    /**
     * 喫下虛化藥丸
     */
    public void eateBlurPill() {
        System.out.println("喫下虛化藥丸");
        transformStateMachine.setCurrentState(new BlurTransform(transformStateMachine));
        transformStateMachine.setFightingPower(transformStateMachine.getFightingPower() + 1000);
        System.out.println("**變身虛化狀態**戰鬥力加1000");
    }

    /**
     * 使用必殺技
     */
    public void useUniqueSkill() {
        System.out.println("使用必殺技");
        System.out.println("黑流牙突--->");
        transformStateMachine.setCurrentState(new NormalTransform(transformStateMachine));
        transformStateMachine.setFightingPower(100);
        System.out.println("**變回初始狀態**戰鬥力變成100");
    }
}

虛化狀態實現類

public class BlurTransform implements ITransformState {

    private TransformStateMachine transformStateMachine;

    public BlurTransform(TransformStateMachine transformStateMachine) {
        this.transformStateMachine = transformStateMachine;
    }

    /**
     * 獲取當前狀態
     * @return
     */
    public State getState() {
        return State.BLUR;
    }

    /**
     * 喫下始解藥丸
     */
    public void eateInitialPill() {
        System.out.println("喫下始解藥丸");
        System.out.println("木有任何變化。。。");
    }

    /**
     * 喫下卍解藥丸
     */
    public void eateSwastikaPill() {
        System.out.println("喫下卍解藥丸");
        System.out.println("木有任何變化。。。");
    }

    /**
     * 喫下虛化藥丸
     */
    public void eateBlurPill() {
        System.out.println("喫下虛化藥丸");
        System.out.println("木有任何變化。。。");
    }

    /**
     * 使用必殺技
     */
    public void useUniqueSkill() {
        System.out.println("使用必殺技");
        System.out.println("王虛的閃光--->");
        transformStateMachine.setCurrentState(new NormalTransform(transformStateMachine));
        transformStateMachine.setFightingPower(100);
        System.out.println("**變回初始狀態**戰鬥力變成100");
    }
}

變身狀態機類

public class TransformStateMachine {

    /**
     * 戰鬥力
     */
    private int fightingPower;
    /**
     * 當前狀態
     */
    private ITransformState currentState;

    /**
     * 初始化
     */
    public TransformStateMachine() {
        this.fightingPower = 100;
        this.currentState = new NormalTransform(this);
    }

    /**
     * 喫下始解藥丸
     */
    public void eateInitialPill() {
        this.currentState.eateInitialPill();
    }

    /**
     * 喫下卍解藥丸
     */
    public void eateSwastikaPill() {
        this.currentState.eateSwastikaPill();
    }

    /**
     * 喫下虛化藥丸
     */
    public void eateBlurPill() {
        this.currentState.eateBlurPill();
    }

    /**
     * 使用必殺技
     */
    public void useUniqueSkill() {
        this.currentState.useUniqueSkill();
    }

    public int getFightingPower() {
        return this.fightingPower;
    }

    public void setFightingPower(int fightingPower) {
        this.fightingPower = fightingPower;
    }

    public State getCurrentState() {
        return this.currentState.getState();
    }

    public void setCurrentState(ITransformState currentState) {
        this.currentState = currentState;
    }
}

測試類

public class Test {
    public static void main(String[] args) {
        TransformStateMachine transformStateMachine = new TransformStateMachine();
        System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
        transformStateMachine.eateInitialPill();
        System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
        transformStateMachine.eateBlurPill();
        System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
        transformStateMachine.eateSwastikaPill();
        System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
        transformStateMachine.eateBlurPill();
        System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
        transformStateMachine.useUniqueSkill();
        System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
        transformStateMachine.eateSwastikaPill();
        System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
        transformStateMachine.useUniqueSkill();
        System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
    }
}

測試結果

當前狀態: NORMAL; 戰鬥力指數: 100

喫下始解藥丸
**變身始解狀態**戰鬥力加200
當前狀態: INITIAL; 戰鬥力指數: 300

喫下虛化藥丸
木有任何變化。。。
當前狀態: INITIAL; 戰鬥力指數: 300

喫下卍解藥丸
**變身卍解狀態**戰鬥力加500
當前狀態: SWASTIKA; 戰鬥力指數: 800

喫下虛化藥丸
**變身虛化狀態**戰鬥力加1000
當前狀態: BLUR; 戰鬥力指數: 1800

使用必殺技
王虛的閃光--->
**變回初始狀態**戰鬥力變成100
當前狀態: NORMAL; 戰鬥力指數: 100

喫下卍解藥丸
**變身卍解狀態**戰鬥力加500
當前狀態: SWASTIKA; 戰鬥力指數: 600

使用必殺技
黑流牙突--->
**變回初始狀態**戰鬥力變成100
當前狀態: NORMAL; 戰鬥力指數: 100

總結

狀態模式一般用來實現有限狀態機。

有限狀態機的英文翻譯是 Finite State Machine,縮寫爲 FSM,簡稱爲狀態機。

狀態機有 3 個組成部分:狀態(State)、事件(Event)、動作(Action)。其中,事件也稱爲轉移條件(Transition Condition)。事件觸發狀態的轉移及動作的執行。

不過,動作不是必須的,也可能只轉移狀態,不執行任何動作。

狀態機的狀態隨着某種事件的發生而改變,事件就是狀態轉移的條件,狀態轉移時可能會有相應的操作要同時進行。

狀態模式的優點

1.如果狀態變更伴隨着複雜的操作,狀態模式能把所有操作封裝到一個類裏,允許狀態轉換邏輯與狀態對象合成一體,而不是某一個巨大的條件語句塊。

2.把每個狀態和動作提取出來,封裝到一個類中,就把着眼點從執行狀態提高到整個對象的狀態,這將使代碼更加結構化,整體意圖更加清晰。

3.以後對狀態的修改之需要修改一個子類,新增一個新的狀態,只需要新增一個狀態類,對原有代碼的修改少。

狀態模式的缺點

1.會增加系統類和對象的個數。

2.一個State的子類至少擁有一個其他子類的信息,各個子類之間產生了依賴。

代碼鏈接












在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章